Thread源码分析之join方法引伸--wait 为什么必定要写在同步代码块中

对于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).
app

就是说wait使用的套路以下

     synchronized (obj) {
         while (<condition does not hold>)
             obj.wait(timeout);
         ... // Perform action appropriate to condition
     }ide

wait使用的套路总结

  1. 必须在 synchronized 同步方法或者 synchronized 同步块中
  2. 必须在while循环中使用

为何wait必须在 synchronized 同步方法或者 synchronized 同步块中?

反证举例:对象o的wait(notify、notifyAll)若是不在synchronized 同步方法或者 synchronized 同步块,那么线程I调用o.wait()时,线程II能够(同一时间内)随时调用o.notify(),以下图函数

线程I处于o.wait()调用后的wait set中,此时代码运行的指针暂存了起来。oop

由于没有互斥同步,线程II能够随时调用o.notify(),那么无论线程I中的condition条件是否知足,都会发生【①通知继续】。ui

随着【①通知继续】的发生,线程II认为:我已经通知过线程I,让线程I继续运行了,个人任务完成了!this

线程I中接收到【①通知继续】,而后会从暂存的位置取出代码运行指针,继续往o.wait()后面运行,而后又跑到while(condition)这里。刚才说了,condition条件可能还不符合,那么就又进入了o.wait(),暂存起来运行指针,线程I进入对象o的wait set区。spa

如今是什么状况?线程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的锁。

相关文章
相关标签/搜索