public class WaitNotifyCase {
public static void main(String[] args) {
// final Object lock = new Object();
final Lock lock = new ReentrantLock();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread A is waiting to get lock");
synchronized (lock) {
try {
System.out.println("thread A get lock");
TimeUnit.SECONDS.sleep(3);
System.out.println("thread A do wait method");
lock.wait();
System.out.println("wait end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("thread B is waiting to get lock");
synchronized (lock) {
System.out.println("thread B get lock");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("thread B do notify method");
lock.notify();
System.out.println("thread A will be alive in five seconds");
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
try {
System.out.println("thread B aready notify thread A");
TimeUnit.SECONDS.sleep(5);
System.out.println("特么我睡醒了");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}).start();
}
}
执行结果:
thread A is waiting to get lock
thread A get lock
thread B is waiting to get lock
thread A do wait method
thread B get lock
thread B do notify method
thread A will be alive in five seconds
thread B aready notify thread A
wait end
Disconnected from the target VM, address: '127.0.0.1:49367', transport: 'socket'
特么我睡醒了
等待方遵循的原则:
- 获取对象的锁
- 不满足条件 就调用wait()方法
- 条件满足继续执行
通知方原则:
- 获取对象的锁
- 改变条件, 然后notify
问题:
- 为什么wait、notify必须被同步块包裹着?
- notify之后 一定会立刻唤醒么?
synchronized代码块通过javap生成的字节码中包含 monitorenter 和 monitorexit 指令。
正如wait方法的注释所说:
This method should only be called by a thread that is the owner of this object's monitor
IllegalMonitorStateException if the current thread is not
the owner of the object's monitor.
lock.wait()
调用时必须拿到当前对象的监视器monitor对象。
1 、在多核环境下,线程A和B有可能同时执行monitorenter指令,并获取lock对象关联的monitor,只有一个线程可以和monitor建立关联,假设线程A执行加锁成功;
2、线程B竞争加锁失败,进入等待队列进行等待;
3、线程A继续执行,当执行到wait方法时, 释放锁
4、线程B获得锁并notify线程A
在HotSpot虚拟机中,monitor采用ObjectMonitor实现。每个线程都有两个ObjectMonitor对象列表,分别为free和used列表,如果当前free列表为空,线程将向全局global list请求分配ObjectMonitor。
ObjectMonitor对象中有两个队列:_WaitSet 和 _EntryList,用来保存ObjectWaiter对象列表;_owner指向获得ObjectMonitor对象的线程。
_WaitSet :处于wait状态的线程,会被加入到wait set;
_EntryList:处于等待锁block状态的线程,会被加入到entry set;
ObjectWaiter
ObjectWaiter对象是双向链表结构,保存了_thread(当前线程)以及当前的状态TState等数据, 每个等待锁的线程都会被封装成ObjectWaiter对象。
wait方法实现
lock.wait()方法最终通过ObjectMonitor的void wait(jlong millis, bool interruptable, TRAPS);实现:
1、将当前线程封装成ObjectWaiter对象node;
2、通过ObjectMonitor::AddWaiter方法将node添加到_WaitSet列表中;
3、通过ObjectMonitor::exit方法释放当前的ObjectMonitor对象,这样其它竞争线程就可以获取该ObjectMonitor对象。
4、最终底层的park方法会挂起线程;
notify方法实现
lock.notify()方法最终通过ObjectMonitor的void notify(TRAPS)实现:
1、如果当前_WaitSet为空,即没有正在等待的线程,则直接返回;
2、通过ObjectMonitor::DequeueWaiter方法,获取_WaitSet列表中的第一个ObjectWaiter节点,实现也很简单。
这里需要注意的是,在jdk的notify方法注释是随机唤醒一个线程,其实是第一个ObjectWaiter节点
参考:http://www.jianshu.com/p/f4454164c017
Java并发编程的艺术