1、Wait 和 Notify
位于Object类中有两套方法wait和notify,这两套方法是如何实现线程间的协作,即线程的等待和通知呢? 先看一下方法描述:
| 方法名称 | 描述 |
|---|---|
| wait() | 调用该方法的线程进入WAITING状态,只有等待另外线程的通知或被中断才会返回,需要注意调用wait()方法后,会释放对象的锁 |
| wait(long) | 超时等待一段时间,这里的参数时间是毫秒,也就是等待长达n毫秒,如果没有通知就超时返回 |
| wait(long, int) | 对于超时时间更细粒度的控制,可以达到纳秒 |
| notify() | 通知一个在对象上等待的线程,使其从wait()方法返回,而返回的前提是该线程获取到了对象的锁 |
| notifyAll() | 通知所有等待在该对象上的线程 |
线程靠调用某个对象的wait方法进行阻塞,线程进入wait set中,通过调动该对象的notify方法从wait set中出来继续执行。
2、为什么必须在同步代码块中使用?
wait必须在synchronized同步代码块中
-
表面上,直接调用wait会抛IllegalMonitorStateException异常,该异常顾名思义,没有拿到monitor(监视器)。该异常类注释如下:
Thrown to indicate that a thread has attempted to wait on an object's monitor or to notify other threads waiting on an object's monitor without owning the specified monitor.
简单翻译,调用wait或者notify,却没有拥有object's monitor(对象监视器,即进入synchronized同步代码)。
-
逻辑上,两个线程调用wait和notify存在竞态条件。
当两个线程竞争同一资源时,如果对资源的访问顺序敏感,就称存在竞态条件。
本来应该线程a先执行wait,线程b在执行notify。结果线程b先执行了,a再执行wait,那a这不永远就在等待了。所以用synchronized的同步代码块,来保证线程a、b对竞态资源的访问,只允许一个线程先取得,也就是同步,保证了线程的执行顺序。
怎么同步代码块就能控制顺序?因为写代码的时候可以让哪个线程先进入同步代码块,即谁先拿到对象的monitor。
-
实现上,这要看JDK关于线程、wait、notify这些native代码了。monitor是用ObjectMonitor实现的,线程有个ObjectMonitor列表,来记录该线程获取对象的monitor情况。
ObjectMonitor中定义了 _WaitSet 、_EntryList 、_owner
- _WaitSet :处于wait状态的线程,会被加入到wait set。
- _EntryList:处于等待锁block状态(等待获取对象monitor)的线程,会被加入到entry set。
- _owner:获取到对象monitor的线程。
在网上找到一个图:
该图描绘出了三者关系和流程:
- 线程等待进入同步代码块(synchronized),进入Entry Set,处于Blocking状态。
- 线程获取到object's monitor,进入同步代码块。
- 线程调用wait方法,释放object's monitor,进入Wait Set,处于Waiting状态。
- object的notifyAll方法被调用,线程出Wait Set。
- The Owner指向拥有object's monitor的线程,处于Runnale状态。
3、通用实践
通用的实践粘贴一段来自Disruptor框架中的代码。
/**
* Blocking strategy that uses a lock and condition variable for {@link EventProcessor}s waiting on a barrier.
* <p>
* This strategy can be used when throughput and low-latency are not as important as CPU resource.
*/
public final class BlockingWaitStrategy implements WaitStrategy
{
// (1) 定义一个object
private final Object mutex = new Object();
@Override
public long waitFor(long sequence, Sequence cursorSequence, Sequence dependentSequence, SequenceBarrier barrier)
throws AlertException, InterruptedException
{
long availableSequence;
if (cursorSequence.get() < sequence)
{
// (1) wait使用
synchronized (mutex)
{
while (cursorSequence.get() < sequence)
{
barrier.checkAlert();
mutex.wait();
}
}
}
while ((availableSequence = dependentSequence.get()) < sequence)
{
barrier.checkAlert();
ThreadHints.onSpinWait();
}
return availableSequence;
}
@Override
public void signalAllWhenBlocking()
{
// (2) notify使用
synchronized (mutex)
{
mutex.notifyAll();
}
}
@Override
public String toString()
{
return "BlockingWaitStrategy{" +
"mutex=" + mutex +
'}';
}
}
(1) 定义一个对象,好使用该对象的monitor、wait、notify。
(2) 的注释中先获取对象的monitor,然后循环等待某种资源,资源不满足时,不断的调用wait释放monitor给notify机会。直到资源满足后,跳出循环。
(3) 的注释中已经获取到对象的monitor,调用notifyAll。