Condition 实现原理

说 Codition 前,需要说下 ConditioObject。 ConditionObject 是同步器 AbstractQueuedSynchronzied 的内部类,因为 Condition 的操作需要关联的锁。 ArrayBlockingQueue 就是 Condition 的具体应用。 Object 中其实 也有 wait ,notify ,notifyAll 等操作, Condition 相当于将 wait ,notify ,notifyAll 转换成想要的对象,将比较难懂的同步操作变成直观可控的对象行为。

应用场景 ArrayBlockingQueue

ArrayBlockingQueue 的构造函数。

/** Main lock guarding all access */
final ReentrantLock lock;/** Condition for waiting takes */
private final Condition notEmpty;/** Condition for waiting puts */
private final Condition notFull;
public ArrayBlockingQueue(int capacity, boolean fair) {if (capacity <= 0)throw new IllegalArgumentException();this.items = new Object[capacity];lock = new ReentrantLock(fair);notEmpty = lock.newCondition();notFull =  lock.newCondition();
}

通过构造函数,可以看到 Condition 的创建时需要关联锁的。

从队列中去取出(take)数据 。

public E take() throws InterruptedException {final ReentrantLock lock = this.lock;lock.lockInterruptibly();try {while (count == 0)notEmpty.await();return dequeue();} finally {lock.unlock();}}

往队列中加入数据 enqueue

private void enqueue(E x) {// assert lock.getHoldCount() == 1;// assert items[putIndex] == null;final Object[] items = this.items;items[putIndex] = x;if (++putIndex == items.length)putIndex = 0;count++;notEmpty.signal();}

可以看主要用了 await signal 等方法。具体代表什么含义?

Condition 实现主要包含三个部分:等待队列、等待、通知

如果了解 AQS 原理可以知道, AQS 中有个同步队列的概念。

等待队列

等待队列和同步队列类似,都是一个 FIFO 队列。队列上每个节点包含一个线程引用,该线程就是 Condition 对象上的等待线程。等待队列结构如下:

Condition 等待队列,也是包含首节点(firstWaiter),和尾节点(tailWaiter),如果一个线程调用了 Condition.await() 方法。那么该线程将会释放锁,并以当前线程构造节点加入等待队列并进入等待状态。

Object 监视器模型

Object 监视器模型 包含了一个同步多路和多个等待队列,结构如下所示:

等待

当调用 Condition 的 await() 方法(或者以 await开头的方法),会使得当前线程进入等待队列,并且释放锁,同时线程的状态变为等待状态。

public final void await() throws InterruptedException {if (Thread.interrupted())throw new InterruptedException();// 当前线程加入等待队列  Node node = addConditionWaiter();// 释放同步状态,也就是释放锁int savedState = fullyRelease(node);int interruptMode = 0;// node 不在节点中会一直 park 阻塞下去。达到等待的效果。while (!isOnSyncQueue(node)) {LockSupport.park(this);if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)break;}if (acquireQueued(node, savedState) && interruptMode != THROW_IE)interruptMode = REINTERRUPT;if (node.nextWaiter != null) // clean up if cancelledunlinkCancelledWaiters();if (interruptMode != 0)reportInterruptAfterWait(interruptMode);}

调用该方法的线程成功获得了锁的线程,也就是同步队列的首节点,该方法将会将该线程构造成节点并加入等待队列中,然后释放同步状态,唤醒同步队列中的后续节点,然后当前节点会进入等待状态。要注意的是,如果等地队列中的节点被唤醒,唤醒节点的线程开始尝试获取同步状态。但是如果不是通过 Condition.signal 进行唤醒的,而是对等待线程进行中断,那么会抛出 InterruptedException。

调用 Condition signal 方法后,当前线程会加入到等待队列,如下图所示:

通知

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

public final void signal() {if (!isHeldExclusively())throw new IllegalMonitorStateException();Node first = firstWaiter;if (first != null)doSignal(first);}
final boolean transferForSignal(Node node) {/** If cannot change waitStatus, the node has been cancelled.*/if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))return false;/** Splice onto queue and try to set waitStatus of predecessor to* indicate that thread is (probably) waiting. If cancelled or* attempt to set waitStatus fails, wake up to resync (in which* case the waitStatus can be transiently and harmlessly wrong).*/Node p = enq(node);int ws = p.waitStatus;if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))LockSupport.unpark(node.thread);return true;}

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

节点从等待队列,移动到同步队列的操作过程如下:

通过调用同步器的 enq(Node node) 方法,等待队列中的头节点线程安全地移动到同步队列中,当节点移动到同步队列后,当前线程再使用 LockSupport 唤醒该节点的线程。

被唤醒的线程,将从 await() 方法中的 while 循环中退出。从 await 方法看

//  当前节点已经在同步队列了,不会在循环下去了while (!isOnSyncQueue(node)) {LockSupport.park(this);if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)break;}

随后调用同步器的 acquireQueued() 方法加入到同步队列的竞争中。

final boolean acquireQueued(final Node node, int arg) {boolean failed = true;try {boolean interrupted = false;for (;;) {final Node p = node.predecessor();if (p == head && tryAcquire(arg)) {setHead(node);p.next = null; // help GCfailed = false;return interrupted;}if (shouldParkAfterFailedAcquire(p, node) &&parkAndCheckInterrupt())interrupted = true;}} finally {if (failed)cancelAcquire(node);}}

成功获取同步状态(获得锁)之后,被唤醒的线程,景从先前调用的 await 方法返回。此时线程已经成功获得了锁。

总结

本文剖析了一下 Condition 的实现原理,等待队列,等待,通知的实现原理。

程序员开发者社区

实现原理_Condition 实现原理相关推荐

  1. grpc通信原理_容器原理架构详解(全)

    目录 1 容器原理架构 1.1 容器与虚拟化 1.2 容器应用架构 1.3 容器引擎架构 1.4 Namespace与Cgroups 1.5 容器镜像原理 2 K8S原理架构 2.1 K8S主要功能 ...

  2. BC之SC:区块链之智能合约——与传统合约的比较以及智能合约模型部署原理、运行原理相关配图

    BC之SC:区块链之智能合约--与传统合约的比较以及智能合约模型部署原理.运行原理相关配图 目录 SC与传统合约的比较 SC模型部署原理.运行原理 SC与传统合约的比较 1.传统合约VS智能合约  特 ...

  3. 真香定律!Android动态换肤实现原理解析,原理+实战+视频+源码

    自己项目中一直都是用的开源的xUtils框架,包括BitmapUtils.DbUtils.ViewUtils和HttpUtils四大模块,这四大模块都是项目中比较常用的.最近决定研究一下xUtils的 ...

  4. mapreduce原理_Hbase Bulkload 原理面试必备

    当需要大批量的向Hbase导入数据时,我们可以使用Hbase Bulkload的方式,这种方式是先生成Hbase的底层存储文件 HFile,然后直接将这些 HFile 移动到Hbase的存储目录下.它 ...

  5. kafka分区与分组原理_Kafka工作原理

    Kafka工作原理 Kafka工作原理 4.1. topic和消息 4.2. Producer 4.3. Consumer 4.4. Kafka核心特性 4.5. consumer.consumer ...

  6. 【重难点】【JUC 04】synchronized 原理、ReentrantLock 原理、synchronized 和 Lock 的对比、CAS 无锁原理

    [重难点][JUC 04]synchronized 原理.ReentrantLock 原理.synchronized 和 Lock 的对比.CAS 无锁原理 文章目录 [重难点][JUC 04]syn ...

  7. bootloader功能介绍/时钟初始化设置/串口工作原理/内存工作原理/NandFlash工作原理...

    bootloader功能介绍 初始化开发板上主要硬件(时钟,内存,硬盘), 把操作系统从硬盘拷贝到内存,然后让cpu跳转到内存中执行操作系统. boot阶段 1.关闭影响CPU正常执行的外设 -关闭看 ...

  8. TRIZ系列-创新原理-14~15-曲面化原理和动态性原理

    一.曲面化原理的表述如下 1)用曲线部件代替直线部件,用球面代替平面,用球体代替立方体: 2)采用滚筒,球体,螺旋体: 3)利用离心力,用旋转物体代替直线运动 由于TRIZ的创新原理是基于专利分析的基 ...

  9. 分类计数原理与分步计数原理_分类计数原理与分步计数原理

    分类计数原理与分步计数原理 <分类计数原理与分步计数原理 ( 一 ) >教学设计 柳州地区民族高级中学 覃艳莉 相关教材 : 人民教育出版社的全日制普通高级中学教科书 ( 必修 ) < ...

最新文章

  1. 「小程序JAVA实战」小程序的举报功能开发(68)
  2. linux lsof/netstat查看进程和端口号相关命令:
  3. 发言倒计时器_单面会议计时器 会议发言倒计时器
  4. 2020五一数学建模比赛总结
  5. 测试显卡cpu中文软件,显卡信息检测工具(GPUinfo)
  6. 信息系统分析与设计----系统分析概述
  7. ARM 汇编详解 -- 体系结构与编程
  8. c语言 获取硬盘号,C语言获得硬盘序列号
  9. 【Struck】论文阅读笔记
  10. numpy dot用法解释
  11. 滴滴快车奖励政策,高峰奖励,翻倍奖励,按成交率,指派单数分级(10月17日~10月23日)...
  12. 最近在装修房子,展示下最近成果
  13. 中国书法名词解释大全
  14. 双网卡同时上网如何设置 双网卡同时上内外网络
  15. 无忧启动源码 仿音速启动
  16. nginx+createrepo搭建局域网yum源
  17. 央视报道:全国22家奶粉厂家69批次…
  18. 项目一 认识Linux 操作系统
  19. 单片机控制继电器电路 程序就很简单了 做一个口的输出就可以了
  20. 一时兴起之matlab学习记录

热门文章

  1. psycopg2 mysql_使用psycopg2操作PostgreSQL数据库之二
  2. livewriter写Blog 神秘失踪?
  3. Atitit 热烈庆祝读经器项目圆满完成
  4. 酷欧天气(CoolWeather)应用源码
  5. js 解析 url参数中文的情况
  6. 如何将Visio图形转换成EPS格式【mark from百度知道】
  7. 在程序中设置最小化、最大化、关闭按钮
  8. 初步的看一下C#窗体程序的直接反汇编代码
  9. 浏览器渲染引擎学习总结
  10. Java struts 2 源码阅读入门