共计 1448 个字符,预计需要花费 4 分钟才能阅读完成。
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 就是标配
正文完