Condition是在java 1.5中才出现的,它用来替代传统的Object的wait()、notify()实现线程间的协作,相比Object的wait()、notify(),使用Condition的await()、signal()这种方式实现线程间协作更加安全和高效。因此一般情况下来说比较推荐使用Condition,阻塞队列实际上是使用了Condition来模拟线程间协作。但在java中Condition接口原理及实现是一个复杂的过程。

Java里 sychronized和Lock+Condtion 都属于管程模型,Condition 在管程模型中代表的就是等待的条件。Condition在Lock的基础上使用,在原来Lock的基础上实现了可以基于多种条件来让线程实现同步的效果增加了多个条件后,我们可以更有针对性,也更灵活的协调多种条件下的线程协调。

每个 Condition 对象包含一个等待队列。等待队列中的节点复用了同步器中同步队列中的节点。Condition 对象的 await 和 signal 操作就是对等待队列以及同步队列的操作。

await 操作:将当前线程构造成节点并加入等待队列中,然后释放同步状态,唤醒同步队列中的后继节点,最后进入等待状态。

signal 操作:会唤醒在等待队列中等待时间最长的节点(首节点),在唤醒节点之前会将节点移到同步队列中。唤醒后的节点尝试竞争锁(自旋)。

首先我们通过一个简单的例子来看下Condition的使用方式:

public static void main(String[] args) {

final ReentrantLock reentrantLock = new ReentrantLock();

final Condition condition = reentrantLock.newCondition();

Thread thread = new Thread((Runnable) () -> {

try {

reentrantLock.lock();

System.out.println("我要等一个新信号" + this);

condition.await();//这里需要注意这里是await而不是wait

}

catch (InterruptedException e) {

e.printStackTrace();

}

System.out.println("拿到一个信号!!" + this);

reentrantLock.unlock();

}, "waitThread1");

thread.start();

Thread thread1 = new Thread((Runnable) () -> {

reentrantLock.lock();

System.out.println("我拿到锁了");

try {

Thread.sleep(3000);

}

catch (InterruptedException e) {

e.printStackTrace();

}

condition.signalAll();

System.out.println("我发了一个信号!!");

reentrantLock.unlock();

}, "signalThread");

thread1.start();

}

运行后,结果如下:

我要等一个新信号lock.ReentrantLockTest$1@a62fc3

我拿到锁了

我发了一个信号!!

拿到一个信号!!

从上面的例子可以看出,Condition的执行方式,是当在线程1中调用await方法后,线程1将释放锁,并且将自己沉睡,等待唤醒,线程2获取到锁后,开始做事,完毕后,调用Condition的signal方法,唤醒线程1,线程1恢复执行。说明Condition是一个多线程间协调通信的工具类,使得某个,或者某些线程一起等待某个条件(Condition),只有当该条件具备( signal 或者 signalAll方法被带调用)时 ,这些等待线程才会被唤醒,从而重新争夺锁。

接下来分析一下Condition接口的原理:

ConditionObject是同步器AbstractQueuedSynchronizer的内部类,因为Condition的操作需要获取相关联的锁,所以作为同步器的内部类也较为合理。每个Condition对象都包含着一个队列,该队列是Condition对象实现等待/通知功能的关键。下面将分析Condition的实现,主要包括:等待队列、等待和通知。

等待队列是一个FIFO的队列,在队列中的每个节点都包含了一个线程引用,该线程就是在Condition对象上等待的线程,如果一个线程调用了Condition.await()方法,那么该线程将会释放锁、构造成节点加入等待队列并进入等待状态。一个Condition包含一个等待队列,Condition拥有首节点(firstWaiter)和尾节点(lastWaiter)。当前线程调用Condition.await()方法,将会以当前线程构造节点,并将节点从尾部加入等待队列。

调用Condition的await()方法(或者以await开头的方法),会使当前线程进入等待队列并释放锁,同时线程状态变为等待状态。当从await()方法返回时,当前线程一定获取了Condition相关联的锁。如果从队列(同步队列和等待队列)的角度看await()方法,当调用await()方法时,相当于同步队列的首节点(获取了锁的节点)移动到Condition的等待队列中。

调用Condition的signal()方法,将会唤醒在等待队列中等待时间最长的节点(首节点),在唤醒节点之前,会将节点移到同步队列中。

public final void signal() {

if (!isHeldExclusively())

throw new IllegalMonitorStateException();

Node first = firstWaiter;

if (first != null)

doSignal(first);

}

调用该方法的前置条件是当前线程必须获取了锁,可以看到signal()方法进行了isHeldExclusively()检查,也就是当前线程必须是获取了锁的线程。接着获取等待队列的首节点,将其移动到同步队列并使用LockSupport唤醒节点中的线程节点从等待队列移动到同步队列。

通过调用同步器的enq(Node node)方法,等待队列中的头节点线程安全地移动到同步队列。当节点移动到同步队列后,当前线程再使用LockSupport唤醒该节点的线程。被唤醒后的线程,将从await()方法中的while循环中退出(isOnSyncQueue(Node node)方法返回true,节点已经在同步队列中),进而调用同步器的acquireQueued()方法加入到获取同步状态的竞争中。成功获取同步状态(或者说锁)之后,被唤醒的线程将从先前调用的await()方法返回,此时该线程已经成功地获取了锁。Condition的signalAll()方法,相当于对等待队列中的每个节点均执行一次signal()方法,效果就是将等待队列中所有节点全部移动到同步队列中,并唤醒每个节点的线程。

讲了很多但其实还是没有说的很详细,这么多内容不是看完就能懂得,需要一定的时间来消化消化。Condition接口实际上也只是java众多接口中的沧海一粟,所以学好java任重而道远。当然,大家如果想学习更多java接口的知识可以查看蛙课网免费的

java condition原理_java中Condition接口原理及实现相关推荐

  1. java final 实例_Java中final实现原理的深入分析(附示例)

    本篇文章给大家带来的内容是关于Java中final实现原理的深入分析(附示例),有一定的参考价值,有需要的朋友可以参考一下,希望对你有所帮助. final在Java中是一个保留的关键字,可以声明成员变 ...

  2. java substring实现_Java中substring()工作原理

    01.substring() 是干嘛的 sub 是 subtract 的缩写,因此 substring 的字面意思就是"把字符串做个减法".这样一分析,是不是感觉方法的命名还是蛮有 ...

  3. java list原理_Java中ArrayList实现原理

    前言 这个分类中,将会写写Java中的集合.集合是Java中非常重要而且基础的内容,因为任何数据必不可少的就是该数据是如何存储的,集合的作用就是以一定的方式组织.存储数据.这里写的集合,一部分是比较常 ...

  4. java collection 用法_Java中Collection接口的用法

    Collection接口是List.Set.Queue接口的父接口 Collection接口定义了可用于操作List.Set和Queue的方法--增删改查 List接口及其实现类--ArrayList ...

  5. java 内省机制_Java反射及 IoC原理、内省机制

    JAVA反射及IoC原理.JAVA内省 1. 反射反射是框架设计的灵魂,使用前提:必须先得到代表的字节码的Class,Class类用于表示.class文件(字节码文件). 1.1 反射概述主要指程序可 ...

  6. java画板抽象类_java 中的 抽象方法 抽象类 和 接口有啥瓜葛

    文章目录 什么是java中的抽象方法 抽象方法是一种特殊的方法: 它只有声明,而没有具体的实现 例如: abstract void eat(); 上面定义了一个方法 eat() ,有修饰关键词 abs ...

  7. java接口那一节是哪的知识_Java中的接口知识汇总

    Java中的接口知识汇总 发布于 2020-4-29| 复制链接 本文给大家汇总介绍了在java中的接口知识,包括为什么要使用接口.什么是接口.抽象类和接口的区别.如何定义接口以及定义接口注意点,希望 ...

  8. java中类的接口是什么_Java中的接口知识汇总

    一.为什么要使用接口 假如有一个需求:要求实现防盗门的功能.门有"开"和"关"的功能,锁有"上锁"和"开锁"的功能. 分 ...

  9. java定义接口的关键字_Java中定义接口的关键字是什么?

    Java中定义接口的关键字是"interface"."interface"是面向对象编程语言中接口操作的关键字,功能是把所需成员组合起来,用来装封一定功能的集合 ...

最新文章

  1. 自动生成低精度深度学习算子
  2. Malware FAQ: How does the Ptrace exploit work o...
  3. java class 生成对象_面向对象编程,你知道Java有哪些创建对象的方式吗?
  4. Android之Bitmap高效缓存以及android缓存策略
  5. java 日期只计算年月日大小_Java 计算两个日期相差多少年月日
  6. WINCE7中RTC时钟芯片BQ32K的开发
  7. MATLAB中的视角处理
  8. 第二次结对编程作业——毕业导师智能匹配
  9. 在双屏软件中,PPT自定义动画注意事项
  10. 通过ip查询详细地址
  11. MATLAB递推最小二乘法(三输入一输出ARX模型、所有样本数据权重为1)
  12. 离散数学-集合论-关系的概念、表示和运算(7)
  13. 戴尔笔记本电源已接通未充电
  14. Linux--内核版本和发行版本
  15. 为什么中国公司要让员工用英文名或者花名?
  16. 二、解线性方程组的直接方法
  17. 永磁同步直线电机仿真实例,仿真教学 maxwell16.0版本 12槽11极
  18. asm中的ClassWriter使用、自定义ClassVisitor和cglib的enhancer使用实例
  19. 鸿蒙不用百度网盘,百度网盘限速有救了!官方新出2种方法,不用开会员
  20. Qt学习day03 Qt设计师(designer) Qt创造器(qtcreator)

热门文章

  1. 计算机显卡输出分辨率怎么设置在哪里,电脑分辨率怎么调呢?怎么设置都不对!...
  2. R星:《荒野大镖客2》首发周末销售额为7.25亿美元
  3. 【Android 屏幕适配】屏幕适配基础概念 ④ ( 屏幕适配限定符 | 手机/平板电脑设备屏幕适配 )
  4. Java设计模式之工厂模式详解
  5. 02134信息系统分析与设计-历年考点整理
  6. matlab 色阶表,色阶表 在线调色 颜色代码表 RGB调色板
  7. Python转换图片bmp格式到jpg格式
  8. EventHandler委托
  9. 10.一键生成个人微信朋友圈数据电子书
  10. js中float运算