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

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

  • 10
    点赞
  • 54
    收藏
    觉得还不错? 一键收藏
  • 8
    评论
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值