深入分析waitnotify为什么要在同步块内

    xiaoxiao2021-03-25  51

    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并发编程的艺术

    转载请注明原文地址: https://ju.6miu.com/read-39948.html

    最新回复(0)