Condition介绍
Condition
Condition是一种多线程通信工具,表示多线程下参与数据竞争的线程的一种状态,主要负责多线程环境下对线程的挂起和唤醒工作。
方法
// ========== 阻塞 ==========
// 造成当前线程在接到信号或被中断之前一直处于等待状态。
void await() throws InterruptedException;
// 造成当前线程在接到信号之前一直处于等待状态。【注意:该方法对中断不敏感】。
void awaitUninterruptibly();
// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
//返回值表示剩余时间,如果在`nanosTimeout` 之前唤醒,那么返回值 `= nanosTimeout - 消耗时间` ,
//如果返回值 `<= 0` ,则可以认定它已经超时了
long awaitNanos(long nanosTimeout) throws InterruptedException;
// 造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
boolean await(long time, TimeUnit unit) throws InterruptedException;
// 造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。
//如果没有到指定时间就被通知,则返回 true ,否则表示到了指定时间,返回返回 false 。
boolean awaitUntil(Date deadline) throws InterruptedException;
// ========== 唤醒 ==========
// 唤醒一个等待线程。该线程从等待方法返回前必须获得与Condition相关的锁。
void signal();
// 唤醒所有等待线程。能够从等待方法返回的线程必须获得与Condition相关的锁。
void signalAll();
原理
Condition 内部维护一个条件队列,在获取锁的情况下,线程调用 await,线程会被放置在条件队列中并被阻塞。直到调用 signal、signalAll 唤醒线程,此后线程唤醒,会放入到 AQS 的同步队列,参与争抢锁资源。
AQS是AbstractQueuedSynchronizer的简称,翻译过来就是抽象队列同步器.
await
调用condition.await()方法会使当前线程进入等待队列并释放锁,同时线程变为等待状态。当从await()方法返回时,一定是获得与condition相关联的锁。
此时AQS主要执行以下动作:
线程1把自己包装成节点,waitStatus设为CONDITION(-2),追加到ConditionObject中的条件队列(每个ConditionObject有一个自己的条件队列);
线程1释放锁,把state设置为0;
然后唤醒等待队列中head节点的下一个节点;
//await
public final void await() throws InterruptedException {
// 1、线程如果中断,那么抛出异常
if (Thread.interrupted())
throw new InterruptedException();
// 2、将当前线程包装成为一个Node节点,加入FIFO队列中
Node node = addConditionWaiter();
// 3、释放锁
int savedState = fullyRelease(node);
int interruptMode = 0;
// 4、判断节点是否在同步队列(注意非Condition队列)中,如果没有,则挂起当前线程,因为该线程尚未具备数据竞争资格
while (!isOnSyncQueue(node)) {
// 5、挂起线程
LockSupport.park(this);
// 6、中断直接返回
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 7、参与数据竞争(非中断时执行)
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 清理条件队列中状态为cancelled的节点
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
signal
另一个线程执行了 condition1.signal之后,主要是做了以下事情:
把条件队列中的第一个节点追加到等待队列中;
把等待队列原来尾节点的waitStatus设置为SIGNAL。
然后继续处理自己的事情,自己的事情处理完成之后,会释放锁,唤醒等待队列中head节点的下一个节点线程进行工作。
public final void signal() {
// 调用signal的线程必须持有独占锁
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
private void doSignal(Node first) {
do {
// 因为first马上就要被转移到同步队列了,所以将first.nextWaiter,作为新的firstWatier。
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
//切断和等待队列的关联
first.nextWaiter = null;
// 如果转移不成功且还有后续节点,那么继续后续节点的转移
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
final boolean transferForSignal(Node node) {
// 判断节点是否已经在之前被取消了
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false;
// 调用 enq 添加到 同步队列的尾部
Node p = enq(node);
int ws = p.waitStatus;
// node 的上一个节点 修改为 SIGNAL 这样后续就可以唤醒自己了
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
使用示例
public class LockConditionDemo {
private static ReentrantLock lock = new ReentrantLock();
private static Condition condition = lock.newCondition();
public static void main(String[] args) throws InterruptedException {
Thread threadA = new Thread(() -> {
System.out.println("threadA start");
lock.lock();
System.out.println("threadA getLock Running");
try {
condition.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
lock.unlock();
System.out.println("threadA end");
});
Thread threadB = new Thread(() -> {
System.out.println("threadB start");
lock.lock();
System.out.println("threadB getLock Running");
condition.signal();
lock.unlock();
System.out.println("threadB end");
});
threadA.start();
TimeUnit.SECONDS.sleep(2);
threadB.start();
}
}
threadA start
threadA getLock Running
threadB start
threadB getLock Running
threadB end
threadA end
Condition介绍的更多相关文章
- ReentrantLock等待通知机制Condition介绍
Object类中的wait(),notify()和notifyAll()可以实现线程的等待通知模型,同样在ReentrantLock中可以借助Condition来完成这种机制.本篇就简要介绍Condi ...
- java中Condition类的详细介绍(详解)
已找不到原文了,还望原文博主看到能告诉小白一下,一定标注原文地址 一 condition 介绍及demo Condition是在java 1.5中才出现的,它用来替代传统的Object的wait(). ...
- Java多线程系列--“JUC锁”06之 Condition条件
概要 前面对JUC包中的锁的原理进行了介绍,本章会JUC中对与锁经常配合使用的Condition进行介绍,内容包括:Condition介绍Condition函数列表Condition示例转载请注明出处 ...
- Java基础篇——线程、并发编程知识点全面介绍(面试、学习的必备索引)
原创不易,如需转载,请注明出处https://www.cnblogs.com/baixianlong/p/10739579.html,希望大家多多支持!!! 一.线程基础 1.线程与进程 线程是指进程 ...
- Java - "JUC"之Condition源码解析
Java多线程系列--“JUC锁”06之 Condition条件 概要 前面对JUC包中的锁的原理进行了介绍,本章会JUC中对与锁经常配合使用的Condition进行介绍,内容包括:Condition ...
- Condition接口
<Java并发编程艺术>读书笔记 Condition介绍 任意一个Java对象,都拥有一组监视器方法(定义在java.lang.Object中),主要包括wait().wait(long ...
- Java多线程系列 JUC锁06 Condition条件
Condition介绍 Condition中提供了一组类似于Object中的监视器方法.与Lock配合可以完成等待通知模式. Lock lock = new ReentrantLock(); Cond ...
- concurrent(四)Condition
参考文档:Java多线程系列--“JUC锁”06之 Condition条件:http://www.cnblogs.com/skywang12345/p/3496716.html Condition介绍 ...
- Java锁--Condition
转载请注明出处:http://www.cnblogs.com/skywang12345/p/3496716.html Condition介绍 Condition的作用是对锁进行更精确的控制.Condi ...
随机推荐
- vue原理相关
vue原理三大模块:响应式.vdom和diff.模板编译 vue原理要点: 1.组件化 组件化的历史:在vue之前已经有组件化的概念了,想asp.jsp.php等就有组件化的概念,nodejs也有组件 ...
- WorkflowAsCode 来了,Apache DolphinScheduler 2.0.2 惊喜发布
文章目录 前言 01 新功能 1 WorkflowAsCode 2 企业微信告警方式支持群聊消息推送 02 优化 1 简化元数据初始化流程 2 删除补数日期中的"+1"(天) 03 ...
- 【活动专辑】Gitee X DolphinScheduler 邀你一起参与 issue 解决赢好礼!
在你参与 DolphinScheduler 项目开发时,是否曾因为这些问题困惑: 对项目感兴趣,但不清楚自己可以为项目做什么 在众多 issue 中,找不到合适自己参与的 issue 任务 认为参与开 ...
- SvelteUI:运用svelte3构建的网页版UI组件库(升级版)
距离上次分享的svelte-ui 1.0已经一月有余,这次带来全新升级完整版svelte-ui 2.0. 这次优化并新增15+个组件.在开发之初借鉴了element-ui组件库,所以在组件结构及语法上 ...
- MySQL查询性能优化七种武器之链路追踪
MySQL优化器可以生成Explain执行计划,我们可以通过执行计划查看是否使用了索引,使用了哪种索引? 但是到底为什么会使用这个索引,我们却无从得知. 好在MySQL提供了一个好用的工具 - opt ...
- gorm tips
约定的列名 type User struct { ID uint // 列名是 `id` Name string // 列名是 `name` Birthday time.Time // 列名是 `bi ...
- a 标签 rel 属性值 opener 的作用
<a> 元素,原英文单词为 anchor 的缩写,所以又称之为锚点元素.锚点元素的 href 属性用来创建通向其他网页.文件.同一页面内的位置.电子邮件地址或任何其他 URL 的超链接. ...
- MybatisPlus核心功能——实现CRUD增删改查操作 (包含条件构造器)
CRUD 官方文档:https://baomidou.com/ (建议多看看官方文档,每种功能里面都有讲解)[本文章使用的mybatisplus版本为3.5.2] 条件构造器 一般都是用service ...
- 【Java】学习路径51-线程组
平时创建线程的时候,系统会默认为线程分组. 我们可以使用 ThreadGroup tg1 = t1.getThreadGroup(); 取得t1的线程组对象. 然后使用getName获得线程组名称. ...
- 【PostgreSQL】PostgreSQL 15移除了Stats Collector
试用即将发行的PostgreSQL 15的人会发现少了一个后台进程: postgres 1710 1 0 04:03 ? 00:00:00 /usr/pgsql-15/bin/postmaster ...