Java中的等待/通知机制

1. 引言

有这样一个场景:线程A在等待变量V符合某个条件,而这个变量V的状态要由另一个线程B去改变。

现在就会有一个关键问题:处于等待中的线程A,如何得知变量V变为了符合自己运行状态?

我们容易想到的一个方法是,让线程A不断地去检查变量V,如果不符合就一直检查,直到符合为止。这个方法是有效的,但是存在如下问题

  • 线程A不停地检查,这会白白浪费CPU资源;
  • 为了降低对CPU资源的耗费,可以让线程A每次检查之后睡眠一段时间,但是这又会导致难以确保及时性,因为如果条件在你睡眠的时候发生了变化,你不能及时感知到。

所以说,循环检测的方法是不理想的。那么有没有更好的方法呢?有,就是等待/通知机制

2. 等待/通知机制的使用

Java中的等待/通知机制,是指一个线程A为了等待变量V符合某个条件,调用了对象O的wait()方法后进入等待状态,加入到对象O的等待队列。而另一个线程B将变量V改编为A所需的状态后,调用对象O的notify()或者notifyAll()方法以通知线程A,线程A收到通知后从对象A的wait()方法返回,得以继续执行。线程A和B通过对象O来完成交互。注意,线程在调用对象O的wait()、notify()、notifyAll方法的时候,必须先获得对象O的锁,在调用之后要释放对象O的锁。

Java等待/通知的经典范式,伪代码:

等待方
synchronized(对象){
    while(条件满足){
        对象.wait();
    }
    对应的处理逻辑
}

通知方
synchronized(对象){
    改变条件
    对象.notify();
}

3. 等待/通知机制的底层原理

等待/通知机制不仅在Java语言中存在,在操作系统中也存在,准确地说,Java中的等待/通知机制就是基于操作系统的等待/通知机制实现的。这篇文章JVM源码分析之wait()和notify()分析了Java中wait()/notify()方法的源码,我这里直接给出结论:

Java的wait()方法直接调用了JVM的本地方法native wait();然后又兜兜转转调用了JVM的另一个本地方法park()来挂起线程;而在park方法中,就是调用了Linux操作系统的等待/通知机制。具体实现方法是条件变量+互斥锁。所以说线程实际上是阻塞在了底层的条件变量上,条件变量必须在互斥锁的配合下才能使用,详见条件变量和互斥锁

pthread_mutex_lock(_mutex)       // 加锁
pthread_cond_wait(_cond, _mutex) // 使线程休眠
pthread_mutex_unlock(_mutex)     // 解锁

条件变量是利用线程间共享的全局变量进行同步的一种机制,主要包括两个动作:一个线程等待"条件变量的条件成立"而挂起;另一个线程使"条件成立"(并给出条件成立信号)。为了防止竞争,条件变量的使用总是和一个互斥锁结合在一起。——百度百科

同理,notify()方法直接就是一个本地方法,兜兜转转调用unpark()方法,最后调用Linux的pthread_cond_signal()方法唤醒阻塞在条件变量上的线程。

pthread_mutex_lock(_mutex)       // 加锁
pthread_cond_signal(_cond)       // 唤醒阻塞的线程
pthread_mutex_unlock(_mutex)     // 解锁

参考文献

JVM源码分析之wait()和notify()

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值