1 为什么wait/notify必须要强制要求放在synchronized中
在日常开发,咱们都晓得wait/notify有着十分固定的一套模板,就是上面的样子,synchronized同步块包裹着Object.wait()
办法,如果不通过同步块包住的话JVM会抛出IllegalMonitorStateException
异样。
synchronized(lock) { while(!condition){ lock.wait(); }}
那么为什么要限度这么写呢?
2 如果Object.wait()/notify不须要同步
假如咱们本人实现了一个BlockingQueue的代码。
如果Object.wait()/notify不须要同步,那么咱们的代码会形如上面这样。
class BlockingQueue { Queue<String> buffer = new LinkedList<String>(); public void give(String data) { buffer.add(data); notify(); // 往队列里增加的时候notify,因为可能有人在等着take } public String take() throws InterruptedException { while (buffer.isEmpty()) // 用while,避免spurious wakeups(虚伪唤醒) wait(); // 当buffer是空的时候就等着他人give return buffer.remove(); }}
如果下面的代码能够执行的话,多线程状况下会呈现一种状况:
- 以后buffer是空的,这时来了一个take的申请,尚未执行到wait语句
- 同时又来了一个give申请,残缺执行完了整个give办法并且发送了notify
- 此时take办法才走到wait,因为它错过了上一个notify,所以会在明明buffer不空的状况下挂起线程,take办法挂起。如果再没有人调用过give办法了,在业务上的体现就会是这个take线程永远也取不到buffer中的内容
3 为什么要在JVM层面抛异样
因为你只有用notify,那就是为了在多线程环境下同步,notify/wait机制自身就是为了多线程的同步而存在的,那就只能配套synchronized,所以为了避免下面状况的产生,就间接强制抛异样来限度开发的代码模式了。
4 如果没有wait/notify
试想一种场景,如果没有wait/notify的挂起唤醒机制,该如何实现BlockingQueue
class BlockingQueue { Queue<String> buffer = new LinkedList<String>(); public void give(String data) { buffer.add(data); } public String take() throws InterruptedException { while(buffer.isEmpty) { sleep(10); } return buffer.remove(); }}
如果没有wait/notify,那么在take的时候只能通过while循环不停轮询判断buffer是否为空来实时获取buffer的最新状态,那么势必会造成两种状况:
- sleep工夫过短,那么线程将始终不行循环抢占CPU,造成CPU打满
- sleep工夫过长,那么将会影响take的时效性
综上,针对于BlockingQueue这样的场景,同步块 + wait/notify 或者 lock + signal/await 就是标配