执行结果:
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并发编程的艺术