对于wait的使用,JDK中给出的解释如下
In other words,
* waits should always occur in loops, like this one:
* <pre>
* synchronized (obj) {
* while (<condition does not hold>)
* obj.wait(timeout);
* ... // Perform action appropriate to condition
* }
* </pre>
* (For more information on this topic, see Section 3.2.3 in Doug Lea's
* "Concurrent Programming in Java (Second Edition)" (Addison-Wesley,
* 2000), or Item 50 in Joshua Bloch's "Effective Java Programming
* Language Guide" (Addison-Wesley, 2001).
就是说wait使用的套路如下
synchronized (obj) {
while (<condition does not hold>)
obj.wait(timeout);
... // Perform action appropriate to condition
}
wait使用的套路总结
- 必须在 synchronized 同步方法或者 synchronized 同步块中
- 必须在while循环中使用
为什么wait必须在 synchronized 同步方法或者 synchronized 同步块中?
反证举例:对象o的wait(notify、notifyAll)如果不在synchronized 同步方法或者 synchronized 同步块,那么线程I调用o.wait()时,线程II可以(同一时间内)随时调用o.notify(),如下图
线程I处于o.wait()调用后的wait set中,此时代码运行的指针暂存了起来。
因为没有互斥同步,线程II可以随时调用o.notify(),那么不管线程I中的condition条件是否满足,都会发生【①通知继续】。
随着【①通知继续】的发生,线程II认为:我已经通知过线程I,让线程I继续运行了,我的任务完成了!
线程I中接收到【①通知继续】,然后会从暂存的位置取出代码运行指针,继续往o.wait()后面运行,然后又跑到while(condition)这里。刚才说了,condition条件可能还不符合,那么就又进入了o.wait(),暂存起来运行指针,线程I进入对象o的wait set区。
现在是什么情况?线程II认为:我已经通知过线程I,让线程I继续运行了,我的任务完成了!所以线程II不会再次通知了。
线程I呢?如上所述再次进入了对象o的wait set,因为线程II不会再次notify线程I,那么线程I将永远存在于wait set区域,再也出不来了!
所以为啥要同步o.wait()和o.notify()?就是为了互斥执行,只有condition满足时,才让线程II通知线程I从wait set区跳出来继续执行,进而继续执行后续的代码。
为什么wait必须在while循环中使用?
如果wait方法不在while循环中使用,那么通知wait结束,没有再次进行条件判断就继续wait后续代码。但是有可能出现condition并不满足的情况,此时代码逻辑就乱了。
★根本原因--wait函数的设计思路
首先wait方法是属于类Object。可以认为任何一个Object对象都有一个内置锁,因为Object类是所有类的祖先类,也就是说所有类的对象都有一个内置锁。想调用wait、notify、notifyAll方法,必须要先获取对象的内置锁。
wait被设计时就是要和notify(或者notifyAll)配合使用的。
使用场景就是:对象o,其有一个属性为a。属性a被线程I和线程II共享,也就是说a为线程I和线程II的共享变量。当a为true时,线程I调用o的wait。线程II可以改变o中a的状态,改变后调用o的notify方法,通知线程I跳出o的wait方法,继续向后执行代码。
总体来说,人家设计的就是这个套路。
t.join() 是谁拿了谁的锁?
之前说过。实质上调用代码1就相当于调用代码2。分析join的源码,结合wait的使用套路,可以得到这个结论。
假设线程I调用t.join()。t.join()最终调用的是t.wait()。
t.join()方法是synchronized 方法。所以线程I中运行到t.join(),是线程I拿到了对象t的锁。
将t.join()换成代码2,也应该以对象t为锁。所以代码2中,仍然是线程I拿到了对象t的锁。
来源:oschina
链接:https://my.oschina.net/u/3866531/blog/2244709