wait、notify、notifyAll
这三个方法都是属于Object的,Java中的类默认继承Object,所以在任何方法中都可以直接调用wait(),notifyAll(),notify(),static方法也一样,
new一个对象再调用。这三个方法必须是在获取到monitor锁的前提下使用,也就是使用ReentrantLock这类锁是不行的,只能是synchronized关键字
内部,否则会出现IllegalMonitorStateException异常。
1、wait:
作用就是进入阻塞状态,准确的说是Waiting状态,如果调用的wait(timeout),进入Timed-Waiting状态,并且会释放monitor锁。
调用wait()有四种被唤醒方式:
1).notify
2).notifyAll
3).wait(timeout)
4).interrupt()
这几种方式我们都比较熟悉,无论你开发过程中有没有用过多线程,提一下interrupt(),因为无论是调用sleep、wait、join等进入阻塞状态下,都是可以
通过抛出异常响应中断。
基本使用:证明wait()会释放monitor锁
public class ThreadClass { private Object object = new Object(); public static void main(String[] args) throws InterruptedException{ ThreadClass threadClass = new ThreadClass(); Thread thread = new Thread(threadClass.new Thread1()); Thread thread1= new Thread(threadClass.new Thread2()); thread.start(); Thread.sleep(100); thread1.start(); } class Thread1 implements Runnable{ @Override public void run() { synchronized (object) { try { System.out.println(Thread.currentThread().getName() + "获取到lock"); object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "继续执行"); } } } class Thread2 implements Runnable{ @Override public void run() { synchronized (object) { object.notify(); System.out.println(Thread.currentThread().getName() + "调用了notify"); } } } }
结果: Thread-0获取到lock Thread-1调用了notify Thread-0继续执行
从结果看,先让Thread-0执行,然后获取Object的monitor锁,然后调用wait进入Waiting状态,Thread-1执行了notify,所以证明wait()释放monitor
锁,否则Thread-1无法进入同步块。Thread-0被唤醒,进入Runnable状态,继而获得CPU使用权,直到执行完成。
wait只释放当前monitor锁
public class ThreadClass { private Object object1 = new Object(); private Object object2 = new Object(); public static void main(String[] args) throws InterruptedException{ ThreadClass threadClass = new ThreadClass(); Thread thread = new Thread(threadClass.new Thread1()); Thread thread1 = new Thread(threadClass.new Thread2()); thread.start(); Thread.sleep(1000); thread1.start(); } class Thread1 implements Runnable{ @Override public void run() { synchronized (object1) { System.out.println(Thread.currentThread().getName() + "获取到object1 monitor lock"); synchronized (object2) { System.out.println(Thread.currentThread().getName() + "获取到object2 monitor lock"); try { System.out.println(Thread.currentThread().getName() + "释放object1 monitor lock"); object1.wait(); } catch (InterruptedException e) { e.printStackTrace(); } } } } } class Thread2 implements Runnable{ @Override public void run() { synchronized (object1) { System.out.println(Thread.currentThread().getName() + "获取到object1 monitor lock"); System.out.println(Thread.currentThread().getName() + "尝试获取到object2 monitor lock"); synchronized (object2) { System.out.println(Thread.currentThread().getName() + "获取到object2 monitor lock"); } } } } }
结果: Thread-0获取到object1 monitor lock Thread-0获取到object2 monitor lock Thread-0释放object1 monitor lock Thread-1获取到object1 monitor lock Thread-1尝试获取到object2 monitor lock
代码执行流程:
①.Thread-0获取object1、object2的monitor锁,但是只释放object1的锁
②.Thread-1在Thread-0是否object1的锁之后,获取到object1的锁,但是无法获取object2的锁
所以证明,wait只释放当前对象的monitor。
wait()工作原理
原理解释:
入口集 Entry Set,等待集Wait Set
1、新启动的线程,会进入Entry Set去竞争monitor锁
2、其中一个线程得到monitor锁,进入红色区域,成为owner
3、此时有两个可能realise,如果调用wait(),进入Wait Set
4、直到被notify/notifyAll唤醒,进入最下面一层
5、此时和第二步一样都要去竞争获取锁,就是Blocked状态
6、程序执行结束,退出
2、notify、notifyAll
从上面的代码中看到notify可以唤醒Waiting状态下的Thread,进入Runnable状态,但不一定立刻获取monitor锁,首先要等待别的Thread释放monitor
锁,然后通过CPU调度是否获取锁。
notify:通过jvm选择唤醒其中一个调用wait()进入Waiting状态下的线程。
notifyAll:唤醒所有调用wait()进入Waiting状态下的线程。
public class ThreadClass { private Object object = new Object(); public static void main(String[] args) throws InterruptedException{ ThreadClass threadClass = new ThreadClass(); Thread thread = new Thread(threadClass.new Thread1()); Thread thread1 = new Thread(threadClass.new Thread1()); Thread thread2= new Thread(threadClass.new Thread2()); thread.start(); thread1.start(); //休眠100ms保证notifyAll()后执行 Thread.sleep(100); thread2.start(); } class Thread1 implements Runnable{ @Override public void run() { synchronized (object) { try { System.out.println(Thread.currentThread().getName() + "获取到lock"); object.wait(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println(Thread.currentThread().getName() + "继续执行"); } } } class Thread2 implements Runnable{ @Override public void run() { synchronized (object) { object.notifyAll(); System.out.println(Thread.currentThread().getName() + "调用了notify"); } } } }
结果: Thread-0获取到lock Thread-1获取到lock Thread-2调用了notify Thread-1继续执行 Thread-0继续执行
代码执行流程:
①.Thread-0和Thread-1启动
②.Thread-0获取到lock,然后执行wait()释放锁
③.Thread-1先进入Blocked状态,等待Thread-0释放锁之后,执行代码,然后执行wait()
④.Thread-2启动,执行notifyAll(),将所有因为wait()进入等待的线程唤醒
⑤.Thread-0和Thread-1竞争monitor锁,然后Thread-1得到锁,执行完成,释放锁
⑥.Thread-0获得锁,执行完成,释放锁
所以证明notifyAll()能够唤醒所有调用wait()进入Waiting状态的线程,如果改成notify(),只会有一个线程继续执行。
总结:
1、wait()、notify()、notifyAll()都需要先获取monitor锁才能执行,否则抛出异常
2、notify只能获取一个调用wait()进入Waiting状态的线程,由jvm决定唤醒哪个,无法提前预知
3、notifyAll唤醒全部调用wait()进入Waiting状态的线程
4、wait()、notify()、notifyAll()都属于Object