目录
前言
wait意思是等待,notify的意思是通知,它们的作用是协同多个线程的执行顺序
因为“抢占式执行”让两个线程的执行顺序充满了不确定性,所以我们希望通过wait和notify来控制线程的执行顺序,使线程之间可以更好的配合。
我们需要知道的是这两个方法都是Object(所以类的老祖宗)的方法
一、wait()方法
wait()做的三件事
1.让当前线程阻塞等待(把这个线程的PCB从就绪队列中放到阻塞队列中),准备接受通知
2.释放当前锁(要想使用wait()或者是notify(),就必须搭配synchronized,需要先获取到锁,然后才能谈wait())
3.需要满足一定的条件才能被唤醒,唤醒后重新尝试获取到这个锁
需要注意的是第一点和第二点需要以原子同时完成
让wait()方法结束等待的条件
1.其他线程调用该对象的notify()方法【最主要的用法】
我们已经知道了wait()和notify()都是Object的方法,就是说,如果线程1中的对象1调用了wait()方法,必须要有一个线程2,也调用对象1的notify()方法才能唤醒线程1,如果线程2调用对象2的notify()方法,是不能唤醒线程1的
public class ThreadDemo18 {
public static void main(String[] args) throws InterruptedException {
Object object=new Object();
object.wait();
object.notify();
}
}
结果为
第一行的灰体带下滑线的意思是非法锁状态
如果没有把wait放到synchronized内部,就能看到这个异常
调用wait的对象和锁对象必须是同一个
public class ThreadDemo18 {
public static void main(String[] args) throws InterruptedException {
Object object=new Object();
synchronized (object){
System.out.println("wait之前");
object.wait();
System.out.println("wait之后");
}
}
}
结果为
从结果我们可以看出现在这个线程已经进入了阻塞队列中,我们也可以从jconsole.exe看出这个线程正在进行等待
如果我们不对这个程序做什么的话这个线程就会永远等待下去,此时我们需要做点什么来使得这个线程等待状态结束,这里我们引入了notify方法
二、notify()方法
关于notify方法的使用
1.也需要放到synchronized中使用
2.notify操作是一次唤醒一个线程,如果是有多个线程都在等待中,调用notify就相当于是随机唤醒了一个,其它线程保持原状
3.调用notify是通知对方被唤醒,但是调用notify方法的线程并不是立刻释放锁,而是要等待当前的synchronized代码块执行完才释放锁(notify本身不会释放锁)
代码实例
public class ThreadDemo19 {
static class WaitTask implements Runnable{
private Object locker=null;
public WaitTask(Object locker) {
this.locker = locker;
}
@Override
public void run() {
//进行wait的线程
synchronized (locker){
System.out.println("wait开始前");
try {
//直接调用wait,相当于this.wait(),也就是针对WaitTask的对象进行等待
//但是我们需要在下面在NotifyTask类中针对同一个对象进行通知
//然而,在NotifyTask中拿到WaitTask的对象并不容易
locker.wait();
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
System.out.println("wait结束");
}
}
}
static class NotifyTask implements Runnable{
private Object locker=null;
public NotifyTask(Object locker) {
this.locker = locker;
}
@Override
public void run() {
//进行notify的线程
synchronized (locker){
System.out.println("notify开始前");
locker.notify();
System.out.println("notify结束");
}
}
}
public static void main(String[] args) throws InterruptedException {
//所以为了解决上述问题,我们需要专门建一个对象,去负责加锁/通知操作
Object locker=new Object();
Thread t1=new Thread(new WaitTask(locker));
Thread t3=new Thread(new WaitTask(locker));
Thread t4=new Thread(new WaitTask(locker));
Thread t2=new Thread(new NotifyTask(locker));
t1.start();
t3.start();
t4.start();
Thread.sleep(3000);
t2.start();
}
}
运行结果为
三、notifyAll()方法
notify方法一次只能唤醒一个线程,而notifyAll可以唤醒多个,虽然多个线程都被唤醒了,但这几个线程执行还是有先后顺序的原因是这几个线程还要竞争锁
public void run() {
//进行notify的线程
synchronized (locker){
System.out.println("notify开始前");
locker.notifyAll();
System.out.println("notify结束");
}
}
运行结果为
2.wait()等待时间超时
3.其它线程调用该等待线程的interrupted()方法,导致wait抛出InterruptedExpectation异常
总结
notify是一次唤醒一个,而notifyAll是一次唤醒一堆,具体用哪个还要看具体场景,一般来说用的是notify
四、wait和sleep的对比【面试题】
1.sleep操作是指定一个固定时间来阻塞等待,而wait既可以指定时间,也可以无限等待
2.wait可以通过notify或interrupt或时间到来唤醒,sleep通过interrupt或时间到唤醒
3.wait的主要用途是为了协调线程之间的先后顺序,而sleep单纯是让线程休眠,并没涉及到多个线程的配合