类似Object监视器方法的Condition接口(详解)
发布时间 - 2026-01-11 01:32:18 点击率:次在《基于线程、并发的基本概念(详解)》中,我们利用synchronized关键字、Queue队列、以及Object监视器方法实现了生产者消费者,介绍了有关线程的一些基本概念。Object类提供的wait的方法和notifyAll方法,与之对应的是Condition接口提供是await和signalAll。await(或wait)是让当前线程进入等待状态并释放锁,signalAll(或notifyAll)则是唤醒等待中的线程,使得等待中的线程有竞争锁的资格,注意只是资格,并不代表被唤醒的线程就一定会获得锁。

Condition接口的具体实现还是在AbstractQueuedSynchronizer中的内部实现的——AbstractQueuedSynchronizer$ConditionObject。ConditionObject中维护了一个“等待队列”,注意这个和AQS同步器维护的“同步队列”不同。AQS所维护的同步队列是当前等待资源(同步状态)的队列,当前线程获取同步状态失败时,同步器会将当前线程以及等待状态等信息构造成一个节点并加入到同步队列中,同时阻塞当前线程,当同步状态被所持有的线程释放时会将同步队列中的首节点唤醒重新获取同步状态。而每个Condition维护一个等待队列,该队列的作用是一个等待signal信号的队列。这两者之间的关系是一个协同的关系,用下图的说明它们之间的协同过程:
1. AQS的同步队列如下图所示,一个头结点head指向队首,一个tail指向队尾,当线程调用lock()方法获取锁而未成功时,线程被构造成节点加入到队尾。(图中NodeA是同步队列的第一个节点,也就是获得同步状态的节点)
2.NodeA调用await()方法时,NodeA从AQS同步队列中移除,自然也就释放了锁,NodeA此时被加入到Condition的等待队列中,等待signal信号,如下图所示。
3.执行完第2步后,此时NodeB在同步队列中处于第一个节点位置,即获取到了锁,如果NodeB此时执行signal(或者signalAll)方法,NodeA将会从Condition等待队列中被移除即被唤醒,加入到同步队列中,此时NodeA仅仅是被唤醒有了在同步队列中争夺资源的资格,并不代表被唤醒后就立即获得锁,如下图所示。
4. 最后NodeB在signal执行完毕后,调用unLock方法释放锁,此时NodeA处于队首,并争夺同步状态。
以上是AQS的“同步队列”和Condition的“等待队列”之间相互协作的过程,下面从源码解析Condition的主要方法await、signal、signalAll。
public final void await() throws InterruptedException{
if (Thread.interrupted()) //线程被中断则抛出中断异常
throw new InterruptedException();
Node node = addConditionWaiter(); //将线程构造为Node节点
long savedState = fullyRelease(node); //释放锁,返回同步状态
int interruptMode = 0;
while (!isOnSyncQueue(node)) { //循环判断当前节点是否在同步队列中
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break; //检查节点在处于等待状态时是否被中断
}
//在跳出了循环,即被signal唤醒后重新加入了同步队列后,开始重新竞争锁
if (acquireQueued(node, savedState) && interruptMode != THROW_IE) //acquireQueued自旋获取锁,具体分析见《2.从AbstractQueuedSynchronizer(AQS)说起(1)——独占模式的锁获取与释放》中对获取同步状态的解析
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters(); //如果节点从等待状态转换为在同步队列中,并且也已经获得了锁,此时将断开此节点后面的等待节点
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
在获取锁的线程调用await时,首先会将线程构造为Node节点并释放锁,此时线程被移出同步队列加入到Condition等待队列中,接着在第7行就会while循环判断节点是否在同步队列中,当没有线程调用signal方法的时候显然线程不在同步队列,并将一直循环,直到有线程调用signal方法该线程才会被唤醒加入到同步队列中,此时才会跳出循环。
signal和signalAll方法的异同在和notify和notifyAll一样。signal只会唤醒等待队列中位于队首的节点使其具有竞争锁的资格,而signalAll则会唤醒等待队列中所有节点使所有节点都具有竞争锁的资格。
public final void signal() {
if (!isHeldExclusively()) //判断当前线程是否持有锁
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first); //唤醒等待队列中的第一个节点
}
对比signalAll方法,不同点在于第6行是唤醒等待队列中的所有节点——doSignalAll(first),不再贴出代码。
private void doSignal(Node first) {
do {
if ((firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) && (first = firstWaiter) != null) //transferForSignal方法将处于等待队列中的节点添加到同步队列中
}
至于doSignalAll则是循环调用transferForSignal使得所有节点都被唤醒加入到同步队列中。
当节点从等待队列中加入到同步队列中时,呼应await中的循环等待节点是否在同步队列中,await和signal的协同配合也就很清晰明了了。
以上这篇类似Object监视器方法的Condition接口(详解)就是小编分享给大家的全部内容了,希望能给大家一个参考,也希望大家多多支持。
# Condition接口
# Java多线程编程中使用Condition类操作锁的方法详解
# 第一个
# 所示
# 是一个
# 如下图
# 也就
# 则是
# 才会
# 给大家
# 并不代表
# 会将
# 基本概念
# 移除
# 的是
# 就会
# 是在
# 将会
# 只会
# 希望能
# 并将
# 仅仅是
相关栏目:
【
网站优化151355 】
【
网络推广146373 】
【
网络技术251813 】
【
AI营销90571 】
相关推荐:
如何彻底删除建站之星生成的Banner?
laravel服务容器和依赖注入怎么理解_laravel服务容器与依赖注入解析
HTML5空格和nbsp有啥关系_nbsp的作用及使用场景【说明】
Laravel路由怎么定义_Laravel核心路由系统完全入门指南
Laravel如何与Docker(Sail)协同开发?(环境搭建教程)
大连 网站制作,大连天途有线官网?
如何获取免费开源的自助建站系统源码?
PythonWeb开发入门教程_Flask快速构建Web应用
开心动漫网站制作软件下载,十分开心动画为何停播?
rsync同步时出现rsync: failed to set times on “xxxx”: Operation not permitted
Laravel PHP版本要求一览_Laravel各版本环境要求对照
装修招标网站设计制作流程,装修招标流程?
Python正则表达式进阶教程_复杂匹配与分组替换解析
Android自定义控件实现温度旋转按钮效果
ChatGPT常用指令模板大全 新手快速上手的万能Prompt合集
java获取注册ip实例
Laravel如何配置中间件Middleware_Laravel自定义中间件拦截请求与权限校验【步骤】
Laravel请求验证怎么写_Laravel Validator自定义表单验证规则教程
PHP 500报错的快速解决方法
JavaScript如何实现类型判断_typeof和instanceof有什么区别
今日头条AI怎样推荐抢票工具_今日头条AI抢票工具推荐算法与筛选【技巧】
Java Adapter 适配器模式(类适配器,对象适配器)优缺点对比
家族网站制作贴纸教程视频,用豆子做粘帖画怎么制作?
node.js报错:Cannot find module 'ejs'的解决办法
如何在建站之星绑定自定义域名?
EditPlus中的正则表达式 实战(1)
Win11怎么关闭透明效果_Windows11辅助功能视觉效果设置
HTML透明颜色代码在Angular里怎么设置_Angular透明颜色使用指南【详解】
油猴 教程,油猴搜脚本为什么会网页无法显示?
Laravel怎么实现支付功能_Laravel集成支付宝微信支付
在线教育网站制作平台,山西立德教育官网?
如何选择PHP开源工具快速搭建网站?
如何在云服务器上快速搭建个人网站?
Laravel怎么发送邮件_Laravel Mail类SMTP配置教程
Laravel如何实现全文搜索_Laravel Scout集成Algolia或Meilisearch教程
Google浏览器为什么这么卡 Google浏览器提速优化设置步骤【方法】
Laravel如何使用Contracts(契约)进行编程_Laravel契约接口与依赖反转
html5如何设置样式_HTML5样式设置方法与CSS应用技巧【教程】
java ZXing生成二维码及条码实例分享
如何用狗爹虚拟主机快速搭建网站?
如何快速配置高效服务器建站软件?
Laravel的契約(Contracts)是什么_深入理解Laravel Contracts与依赖倒置
香港服务器网站生成指南:免费资源整合与高速稳定配置方案
怎样使用JSON进行数据交换_它有什么限制
Laravel广播系统如何实现实时通信_Laravel Reverb与WebSockets实战教程
javascript中的数组方法有哪些_如何利用数组方法简化数据处理
Win11怎么设置默认图片查看器_Windows11照片应用关联设置
Laravel storage目录权限问题_Laravel文件写入权限设置
移动端脚本框架Hammer.js
,怎么在广州志愿者网站注册?

