等待跟唤醒
概述:
- 等待和唤醒,指的是两个线程或多个线程之间的相互等待或唤醒
- Java中提供了几个方法可以实现线程的等待和唤醒,这几个方法都来自Object类
常用方法:
void wait() 让当前线程释放锁并进入等待,直到其他线程调用锁的 notify() 或 notifyAll()方法
void notify() 唤醒正在等待对象监视器(锁)的单个线程
void notifyAll() 唤醒正在等待对象监视器(锁)的所有线程
wait:线程等待
- 在一个同步代码块内,线程调用锁对象的 wait() 方法,让自己释放同步锁,并进入等待状态。只有其他线程调用了锁对象的 notify() 方法,等待的线程才可能解除等待,重新参与锁对象的竞争,线程如果再次得到锁,就可以从wait处继续向下运行
notify:线程唤醒
- 在一个同步代码块内,线程调用锁对象的 notify() 方法,唤醒正在等待的另一个线程。(被唤醒的线程需要重新抢夺锁,成功之后可以从wait处继续往下执行。)如果有多个线程同时在等待,notify() 方法只会随机唤醒某个线程,如果想唤醒所有等待的线程,可以使用notifyAll() 方法
注意事项
- 等待和唤醒是多个线程之间的操作
- wait() ,notify() ,notifyAll() 方法 必须在同步代码块中使用锁对象调用
- notify() 和 notifyAll() 方法并不释放锁,只是告诉在等待锁的线程可以去参与获得锁的竞争了,但被唤醒的线程不是马上得到锁,因为锁还在别人手里没释放 :::
线程死锁
容易死锁条件
- 有多把锁
- 有多个线程
- 有同步代码块嵌套 :::
线程的状态
在Thread.State枚举类中,定义了线程的6种状态:
- 新建状态(NEW)
- 可运行状态(RUNNABLE)
- 终止状态(TERMINATED)
- 阻塞状态(BLOCKED)
- 无限等待状态(WAITING)
- 计时等待(TIMED_WAITING)
线程池
好处:
- 资源利用
- 提高效率
创建线程池
Executors类是线程池的工具类,通过Executors工具类可以创建线程池
常用方法:
static ExecutorService newFixedThreadPool(int nThreads) 建一个线程池,参数为池中的线程数
使用线程池
- ExecutorService代表线程池,该类中提供了 submit 方法用于处理提交的任务
调用 submit(任务) 方法时,线程池会分配池中空闲的线程去执行对应的任务
常用方法
submit(Runnable task) 提交Runnable类型的任务
- submit(Callable
task) 提交Callable类型的任务 -
创建任务的两种方式
实现Runnable接口,重写run方法
实现 Callable<返回值类型> 接口,重写call方法
Callable方式好处:
有返回值
- Future f = 线程池.submit(Callable任务);
- 返回值类型 变量 = f.get()
- 可以抛异常。
线程池执行原理:
- 任务通过submit方法提交给线程池
- 线程池分配线程执行任务,执行结束后线程放回线程池,等待执行下次任务
- 当线程池中没有空闲线程时,任务进入任务队列中等待,直到有空闲的线程去执行任务
:::
public static void main(String[] args) {
Object obj = new Object();
//开始游戏线程
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("准备开黑");
synchronized (obj) {
try {
//释放obj锁,并进入等待
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("游戏开始");
}
});
thread1.start();
//加载游戏线程
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
System.out.println("玩家加载");
for (int i = 1; i <= 5; i++) {
System.out.println("玩家" + i + "准备就绪");
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
System.out.println("加载完毕");
synchronized (obj) {
//唤醒正在等待的obj锁对象的线程,被唤醒的线程需要重新获取锁,然后往下执行
obj.notify();
}
}
});
thread2.start();
}
```java //线程池的线程默认名字为pool-线程池编号-thread-线程编号 public static void main(String[] args) { //获取线程池对象,参数为线程池线程数量 //线程数量设置要看具体业务 ExecutorService pool = Executors.newFixedThreadPool(3); pool.submit(new DemoExecutors()); pool.submit(new DemoExecutors()); pool.submit(new DemoExecutors()); pool.submit(new DemoExecutors()); pool.submit(new DemoExecutors()); pool.submit(new DemoExecutors()); //销毁线程池 实际开发中一般不用 //pool.shutdown(); }/*
两个线程交替打印奇偶数
偶数线程
如果是偶数,打印并自增,通知notify另外一条线程打印
如果是奇数,进入等待wait
奇数线程
如果是奇数,打印并自增,通知notify另外一条线程打印
如果是偶数,进入等待wait
*/
static final Object obj = new Object();
//共享静态变量
static int i = 0;
public static void main(String[] args) {
Thread thread1 = new Thread(new Runnable() {
@Override
public void run() {
Thread thread1 = Thread.currentThread();
thread1.setName("偶数线程");
String name = thread1.getName();
synchronized (obj) {
while (i <= 10) {
if (i % 2 == 0) {
System.out.println(name + ":" + i);
//自增并通知另外一条线程打印
i++;
obj.notify();
} else {
//进入等待
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
});
Thread thread2 = new Thread(new Runnable() {
@Override
public void run() {
Thread thread2 = Thread.currentThread();
thread2.setName("奇数线程");
String name = thread2.getName();
synchronized (obj) {
while (i <= 10) {
if (i % 2 != 0) {
System.out.println(name + ":" + i);
i++;
obj.notify();
} else {
try {
obj.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
});
thread1.start();
thread2.start();
}
public class DemoExecutors implements Runnable{ @Override public void run() { try { Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } Thread thread = Thread.currentThread(); String name = thread.getName(); System.out.println(“[“ + name + “]: 执行了” ); } }
```java
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
//创建任务
DemoCallable callable = new DemoCallable();
//提交任务到线程池,得到线程执行返回值,
//执行submit得到Future对象,里面封装了方法的返回值
Future<Integer> submit = pool.submit(callable);
//调用Future对象的get方法,阻塞等待线程的返回, 需抛出异常
Integer i = submit.get();
System.out.println("休眠了:" + i + "s");
}
public class DemoCallable implements Callable<Integer> {
@Override
public Integer call() throws Exception {
System.out.println("线程执行");
Random ran = new Random();
int i = ran.nextInt(3) + 1;
Thread.sleep(i * 1000);
System.out.println("休眠结束");
return i;
}
}
/*
需求:使用线程池方式创建两个线程任务:分段计算1~20000之间的数字和。
1.线程1计算1~10000之间的数字和,并返回结果。
2.线程2计算10001~20000之间的数字和,并返回结果。
3.提交任务,获取计算结果进行合并,打印最终结果。
*/
public class DemoCallable {
public static void main(String[] args) throws ExecutionException, InterruptedException {
//创建线程池
ExecutorService pool = Executors.newFixedThreadPool(2);
DemoSum demoSum = new DemoSum();
Future<Integer> future1 = pool.submit(() -> demoSum.sum(1, 100));
Future<Integer> future2 = pool.submit(() -> demoSum.sum(100, 201));
Integer num1 = future1.get();
Integer num2 = future2.get();
System.out.println("和为:" + (num1 + num2));
}
}
class DemoSum {
public int sum(int a, int b) {
int num = 0;
for (int i = a; i < b; i++) {
num += i;
}
return num;
}
}