深入分析wait/notify为什么要在同步块内

版权声明:人工智能大佬群号:467123855 。 www.6aiq.com AIQ-机器学习大数据技术社区 全国最专业的机器学习大数据技术社区。 https://blog.csdn.net/lsgqjh/article/details/61915074
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并发编程的艺术

展开阅读全文

没有更多推荐了,返回首页