java并发condition_Java并发之Condition的实现分析

一、Condition的概念

介绍

回忆 synchronized 关键字,它配合 Object 的 wait()、notify() 系列方法可以实现等待/通知模式。

对于 Lock,通过 Condition 也可以实现等待/通知模式。

Condition 是一个接口。

Condition 接口的实现类是 Lock(AQS)中的 ConditionObject。

Lock 接口中有个 newCondition() 方法,通过这个方法可以获得 Condition 对象(其实就是 ConditionObject)。

因此,通过 Lock 对象可以获得 Condition 对象。

Lock lock = new ReentrantLock();Condition c1 = lock.newCondition();Condition c2 = lock.newCondition();

二、Condition的实现分析

实现

ConditionObject 类是 AQS 的内部类,实现了 Condition 接口。

public class ConditionObject implements Condition, java.io.Serializable { private transient Node firstWaiter; private transient Node lastWaiter; ...

可以看到,等待队列和同步队列一样,使用的都是同步器 AQS 中的节点类 Node。

同样拥有首节点和尾节点,

每个 Condition 对象都包含着一个 FIFO 队列。

结构图:

a3318c941136e31422f9e0be7c9e8802.gif

Java并发之Condition的实现分析-1.jpg (9.01 KB, 下载次数: 0)

2020-6-7 22:32 上传

等待

调用 Condition 的 await() 方法会使线程进入等待队列,并释放锁,线程状态变为等待状态。

public final void await() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); //释放同步状态(锁) int savedState = fullyRelease(node); int interruptMode = 0; //判断节点是否放入同步对列 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 cancelled unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode);}

分析上述方法的大概过程:

将当前线程创建为节点,加入等待队列;释放锁,唤醒同步队列中的后继节点;while循环判断节点是否放入同步队列:

没有放入,则阻塞,继续 while 循环(如果已经中断了,则退出)放入,则退出 while 循环,执行后面的判断退出 while 说明节点已经在同步队列中,调用 acquireQueued() 方法加入同步状态竞争。竞争到锁后从 await() 方法返回,即退出该方法。

a3318c941136e31422f9e0be7c9e8802.gif

Java并发之Condition的实现分析-2.jpg (30.35 KB, 下载次数: 0)

2020-6-7 22:32 上传

addConditionWaiter() 方法:

private Node addConditionWaiter() { Node t = lastWaiter; if (t != null && t.waitStatus != Node.CONDITION) { //清除条件队列中所有状态不为Condition的节点 unlinkCancelledWaiters(); t = lastWaiter; } //将该线程创建节点,放入等待队列 Node node = new Node(Thread.currentThread(), Node.CONDITION); if (t == null) firstWaiter = node; else t.nextWaiter = node; lastWaiter = node; return node;}

过程分析:同步队列的首节点移动到等待队列。加入尾节点之前会清除所有状态不为 Condition 的节点。

通知

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

public final void signal() { //判断是否获取了锁 if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignal(first);}

过程:

先判断当前线程是否获取了锁;然后对首节点调用 doSignal() 方法。private void doSignal(Node first) { do { if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; first.nextWaiter = null; } while (!transferForSignal(first) && (first = firstWaiter) != null);}

过程:

修改首节点;调用 transferForSignal() 方法将节点移动到同步队列。final boolean transferForSignal(Node node) { //将节点状态变为0  if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; //将该节点加入同步队列 Node p = enq(node); int ws = p.waitStatus; //如果结点p的状态为cancel 或者修改waitStatus失败,则直接唤醒 if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL)) LockSupport.unpark(node.thread); return true;}

调用同步器的 enq 方法,将节点移动到同步队列,

满足条件后使用 LockSupport 唤醒该线程。

a3318c941136e31422f9e0be7c9e8802.gif

Java并发之Condition的实现分析-3.jpg (29.66 KB, 下载次数: 0)

2020-6-7 22:32 上传

当 Condition 调用 signalAll() 方法:

public final void signalAll() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignalAll(first);}private void doSignalAll(Node first) { lastWaiter = firstWaiter = null; do { Node next = first.nextWaiter; first.nextWaiter = null; transferForSignal(first); first = next; } while (first != null);}

可以看到 doSignalAll() 方法使用了 do-while 循环来唤醒每一个等待队列中的节点,直到 first 为 null 时,停止循环。

一句话总结 signalAll() 的作用:将等待队列中的全部节点移动到同步队列中,并唤醒每个节点的线程。

总结

整个过程可以分为三步:

第一步:一个线程获取锁后,通过调用 Condition 的 await() 方法,会将当前线程先加入到等待队列中,并释放锁。然后就在 await() 中的一个 while 循环中判断节点是否已经在同步队列,是则尝试获取锁,否则一直阻塞。

第二步:当线程调用 signal() 方法后,程序首先检查当前线程是否获取了锁,然后通过 doSignal(Node first) 方法将节点移动到同步队列,并唤醒节点中的线程。

第三步:被唤醒的线程,将从 await() 中的 while 循环中退出来,然后调用 acquireQueued() 方法竞争同步状态。竞争成功则退出 await() 方法,继续执行。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/511658.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【新功能】开放搜索多路召回技术解读

简介:多路召回就是指采用不同的策略、特征或者简单模型,分别召回一部分候选集,然后再把这些候选集混合在一起后供后续排序模型使用的策略,本文将介绍开放搜索平台上的多路召回技术是如何深度提升搜索效果的。 背景 所谓的“多路…

CCO x Hologres:实时数仓高可用架构再次升级,双11大规模落地

简介:本文将会介绍今年是如何在去年基础上进行实时数仓高可用架构升级,并成功大规模落地双11。 作者 | 梅酱 来源 | 阿里技术公众号 一 2021年双11总结 2021年阿里巴巴双11期间,由CCOHologres构建的高可用实时数仓经过2年的迭代&#xff0…

MLPerf世界纪录技术分享:通过模型压缩优化取得最佳性能

作者 | 刘姝 供稿 | 浪潮 MLPerf竞赛由图灵奖得主大卫帕特森(David Patterson)联合谷歌、斯坦福、哈佛大学等单位共同成立,是国际上最有影响力的人工智能基准测试之一。在MLPerf V0.7推理竞赛开放赛道中,浪潮信息通过模型压缩优…

Serverless 应用优化四则秘诀

简介:Serverless 架构下,虽然我们更多精力是关注我们的业务代码,但是实际上对于一些配置和成本也是需要进行关注的,并且在必要的时候,还需要根据配置与成本进行对我们的 Serverless 应用进行配置优化和代码优化。 Ser…

懵了,构建一个 Docker 镜像花 60 分钟?如何提高效率?

作者 | Andy来源 | 进击云原生最近,有一个需求:向镜像构建管道添加一个参数,以允许用户在构建时配置超时时间。我们计划在构建时配置 10 分钟的默认超时,并且允许用户覆盖此配置,因为他们的某些镜像构建需要长达 60 分…

java实现短信上行源码_Java 发送短信验证码 示例源码

【实例简介】执行前请先设置修改 src/test.java 文件//用户名private static String Uid "uid";//接口安全秘钥(不是登录密码)private static String Key "key";//手机号码,多个号码如13800000000,13800000001,13800000002private static Str…

Android项目架构设计深入浅出

简介:本文结合个人在架构设计上的思考和理解,介绍如何从0到1设计一个大型Android项目架构。 作者 | 璞珂 来源 | 阿里技术公众号 前言:本文结合个人在架构设计上的思考和理解,介绍如何从0到1设计一个大型Android项目架构。 一 引…

技术实践第四期|解读移动开发者日常-性能监控平台应用

简介:应用性能监控平台是用来帮助客户提升应用性能质量和稳定性的重要环节,本人作为一名移动端开发者有着丰富的使用和运维经验,希望通过本文分享过往的心得和使用经验,让我参与开发的U-APM这款产品中,作为借鉴可以在中…

彻底理解操作系统:CPU与实模式

作者 | 陆小风来源 | 码农的荒岛求生‍对于人类来说,我们不喜欢拐弯抹角,喜欢更直接的东西,“有话直说”、“没有中间商赚差价”、“简洁的设计”等等,然而对于计算机,尤其是对内存管理来说则恰恰相反,在这…

边缘计算的 4 种类型都有哪些?

作者 | Addo Zhang来源 | 云原生指北本篇文章译自 SUNKU RANGANATH 的 4 Types of Edge Computing - Broadly Categorized。文章通过 往返终端设备和数据中心的延迟 来对边缘计算的类型进行大致的分类,通俗易懂,方便大家对边缘计算有个大概的了解。边缘计…

OpenKruise v1.0:云原生应用自动化达到新的高峰

简介:OpenKruise 是针对 Kubernetes 的增强能力套件,聚焦于云原生应用的部署、升级、运维、稳定性防护等领域。 云原生应用自动化管理套件、CNCF Sandbox 项目 -- OpenKruise,近期发布了 v1.0 大版本。 OpenKruise[1] 是针对 Kubernetes 的…

Serverless Kubernetes 落地实践

简介:如何通过原生 Kubernetes 提供 Serverless 能力?如何借力丰富的云原生社区生态?本文将给大家介绍一下我们在 Serverless Kubernetes 上的落地实践。 作者:元毅 导读 Kubernetes 作为当今云原生业界标准,具备良…

如何在零停机的情况下迁移 Kubernetes 集群

简介:本文将通过集群迁移的需求、场景以及实践方式,介绍如何基于阿里云容器服务 ACK,在零停机的情况下迁移 Kubernetes 集群。 作者:顾静(子白)|阿里云高级研发工程师;谢瑶瑶&#…

轻松理解 Docker 网络虚拟化基础之 veth 设备!

作者 | 张彦飞allen来源 | 开发内功修炼最近我又对网络虚拟化技术产生了浓厚的兴趣,迫切想搞明白在 Docker 等虚拟技术下,网络底层是如何运行的。不得不说,网络虚拟化技术是我给自己抛的又一个大坑。虽然我自认为把原生 Linux 网络实现过程理…

如何做好数字化体验管理,了解一下?

简介:本文主要分为三部分,第一部分是数字化体验的必要性,从数字化体验管理对业务的影响和数字化体验管理对企业的价值两个方面来介绍其必要性;第二部分,ARMS 在数字化体验管理上的产品能力介绍;第三部分&am…

深信服智能边缘计算平台与 OpenYurt 落地方案探索与实践

简介:本文将介绍边缘计算落地的机遇与挑战,以及边缘容器开源项目 OpenYurt 在企业生产环境下的实践方案。 作者:赵震,深信服云计算开发工程师,OpenYurt 社区 Member 编者案:在 5G、物联网等新技术的持续推…

云原生的本手、妙手和俗手

作者 | 步尔斯特来源 | 步尔斯特刚刚看到一个话题:Java大军,真的开始把目光从Spring Cloud转向云原生系的k8s Istio了么?乍一看,有一丝不解,Java大军?Spring Cloud?Kubernetes?Serv…

云原生背景下故障演练体系建设的思考与实践—云原生混沌工程系列之指南篇

简介:生产环境的突袭演练是我们迈出的艰难但有力的一步,锻炼了研发运维人员的应急响应能力,在真实用户场景下锤炼系统,推进了产品的轮班制度,提升了云原生底座的稳定性和竞争力。 作者:智妍(郑…

Spring Boot Serverless 实战系列“部署篇” | Mall 应用

简介:本篇中为各位讲述如何将 Mall 应用部署到函数计算平台上。为了让分析更有代表性,我选择了 Github 上 star 数超过 50k 的电商应用 mall 作为示例。 作者:西流 导读:Spring Boot 是基于 Java Spring 框架的套件,它…

押宝云原生,Ampere Computing打响服务器芯片变革关键一枪

“闯龙潭,越虎穴”这句话用来形容在服务器领域披荆斩棘的Arm架构服务器器芯片似乎最为合适不过。所谓“龙潭”“虎穴”指的便是雄霸服务器芯片市场数载的intel与AMD,而他们所属的x86架构,更是数据中心芯片领域的绝对主流。然而云原生时代的到…