高并发编程-Condition深入解析

概述

Condition接口位于java.util.concurrent.locks包下,实现类有 AbstractQueuedLongSynchronizer.ConditionObject和 AbstractQueuedSynchronizer.ConditionObject。Condition将Object监视器方法(wait、notify和 notifyAll)分解成截然不同的对象,以便通过将这些对象与任意Lock实现组合使用。其中,Lock替代了synchronized方法的使用及作用,Condition替代了Object监视器方法的使用及作用。Condition的await方法代替Object的wait;Condition的signal方法代替Object的notify方法;Condition的signalAll方法代替Object的notifyAll方法。Condition实例在使用时需要绑定到一个锁上,可以通过newCondition方法获取Condition实例。Condition实现可以提供不同于Object监视器方法的行为和语义,比如受保证的通知排序,或者在执行通知时不需要保持一个锁。

样例代码

下面的代码演示了Condition简单使用的样例。

public class ConditionDemo {
    @Test    
    public void test() {
        final ReentrantLock reentrantLock = new ReentrantLock();
        final Condition condition = reentrantLock.newCondition();        
        new Thread(new Runnable() {
            @Override            
            public void run() {                
                try {
                    reentrantLock.lock();
                    System.out.println(Thread.currentThread().getName() + "在等待被唤醒");
                    condition.await();
                    System.out.println(Thread.currentThread().getName() + "恢复执行了");
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    reentrantLock.unlock();
                }
            }
        }, "thread1").start();        
        new Thread(new Runnable() {
            @Override            
            public void run() {                
                try {
                    reentrantLock.lock();
                    System.out.println(Thread.currentThread().getName() + "抢到了锁");
                    condition.signal();
                    System.out.println(Thread.currentThread().getName() + "唤醒其它等待的线程");
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    reentrantLock.unlock();
                }
            }
        }, "thread2").start();
    }
}

输出结果如下所示:

thread1在等待被唤醒
thread2抢到了锁
thread2唤醒其它等待的线程
thread1恢复执行了

创建Condition实例

通过Lock接口实现类的newCondition方法获取Condition实例,例如如下代码:

ReentrantLock reentrantLock = new ReentrantLock();
Condition condition = reentrantLock.newCondition();

常用方法

await()

调用await方法后,当前线程在接收到唤醒信号之前或被中断之前一直处于等待休眠状态。调用此方法时,当前线程保持了与此Condition有关联的锁,调用此方法后,当前线程释放持有的锁。此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁,在线程返回时,可以保证它保持此锁。

await(long time,TimeUnit unit)

调用此方法后,会造成当前线程在接收到唤醒信号之前、被中断之前或到达指定等待时间之前一直处于等待状态。调用此方法时,当前线程保持了与此Condition有关联的锁,调用此方法后,当前线程释放持有的锁。time参数为最长等待时间;unit参数为time的时间单位。如果在从此方法返回前检测到等待时间超时,则返回 false,否则返回true。此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁,在线程返回时,可以保证它保持此锁。

awaitNanos(long nanosTimeout)

该方法等效于await(long time,TimeUnit unit)方法,只是等待的时间是 nanosTimeout指定的以毫微秒数为单位的等待时间。该方法返回值是所剩毫微秒数的一个估计值,如果超时,则返回一个小于等于0的值。可以根据该返回值来确定是否要再次等待,以及再次等待的时间。

awaitUninterruptibly()

调用此方法后,会造成当前线程在接收到唤醒信号之前一直处于等待状态。如果在进入此方法时设置了当前线程的中断状态,或者在等待时,线程被中断,那么在接收到唤醒信号之前,它将继续等待。当最终从此方法返回时,仍然将设置其中断状态。调用此方法时,当前线程保持了与此Condition有关联的锁,调用此方法后,当前线程释放持有的锁。此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁,在线程返回时,可以保证它保持此锁。

awaitUntil(Date deadline)

调用此方法后,会造成当前线程在接收到唤醒信号之前、被中断之前或到达指定最后期限之前一直处于等待休眠状态。调用此方法时,当前线程保持了与此Condition有关联的锁,调用此方法后,当前线程释放持有的锁。此方法可以返回当前线程之前,都必须重新获取与此条件有关的锁,在线程返回时,可以保证它保持此锁。

signal()

唤醒一个等待线程,如果所有的线程都在等待此条件,则选择其中的一个唤醒。在从await返回之前,该线程必须重新获取锁。

signalAll()

唤醒所有等待线程,如果所有的线程都在等待此条件,则唤醒所有线程。 在从await返回之前,每个线程必须重新获取锁。

底层实现原理

这里以AQS内部的ConditionObject实现为例,分析底层实现原理,这部分内容已在高并发编程-CyclicBarrier深入解析源码解析后半部分深入分析过,这里不再做具体分析。

原文发布于微信公众号 - JavaQ(Java-Q)

原文发表时间:2018-10-08

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏菩提树下的杨过

java 中的异步回调

异步回调,本来在c#中是一件极为简单和优雅的事情,想不到在java的世界里,却如此烦琐,先看下类图: ? 先定义了一个CallBackTask,做为外层的面子工...

3137
来自专栏DannyHoo的专栏

block和代理

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u010105969/article/details/...

1443
来自专栏cloudskyme

什么是线程安全

什么是线程安全?       如果你的代码所在的进程中有多个线程在同时运行,而这些线程可能会同时运行这段代码。如果每次运行结果和单线程运行的结果是一样的,而且...

3238
来自专栏好好学java的技术栈

深入线程Thread类的start()方法和run()方法

java的线程是通过java.lang.Thread类来实现的。VM启动时会有一个由主方法所定义的线程。可以通过创建Thread的实例来创建新的线程。每个线程都...

1020
来自专栏angularejs学习篇

angularjs学习第六天笔记(指令简介学习)

  您好,由于周末有事情,没哟学习angularjs,几天晚上开始继续学习angularjs,坚持加油每一天。谢谢

891
来自专栏决胜机器学习

PHP开发过程的那些坑(二) ——PHP empty函数

PHP开发过程的那些坑(二)——PHP empty函数 (原创内容,转载请注明来源,谢谢) 坑: 在工作过程中,常用empty()函数判断数组是否为空,为空时返...

3828
来自专栏好好学java的技术栈

「附数据结构资源」玩转java并发(六):深入线程Thread类的start()方法和run()方法

java的线程是通过java.lang.Thread类来实现的。VM启动时会有一个由主方法所定义的线程。可以通过创建Thread的实例来创建新的线程。每个线程都...

1072
来自专栏博客园

Redis命令与配置

    slaveof  127.0.0.1 6379(设置Mater的Host以及Port)

1614
来自专栏Laoqi's Linux运维专列

for 循环,while循环,break,continue,exit

3718
来自专栏对角另一面

lodash源码分析之缓存方式的选择

本文为读 lodash 源码的第八篇,后续文章会更新到这个仓库中,欢迎 star:pocket-lodash

2029

扫码关注云+社区

领取腾讯云代金券