Java并发(十一):Condition条件
先做总结:
1、为什么使用Condition条件?
synchronized配合Object的wait()、notify()系列方法可以实现等待/通知模式。
Lock提供了条件Condition,对线程的等待、唤醒操作更加详细和灵活。
Condition的作用是对锁进行更精确的控制。Condition中的await()方法相当于Object的wait()方法,Condition中的signal()方法相当于Object的notify()方法,Condition中的signalAll()相当于Object的notifyAll()方法。
2、Condition条件实现原理:
(1)lock.newCondition(),new一个ConditionObject对象,ConditionObject有一个单向等待队列
(2)condition等待队列:线程已经拿到锁,但是因为不满足某个条件,被放入等待队列并释放锁,不能获取锁
AQS同步队列:线程抢锁失败,被放入AQS阻塞队列,等前面线程释放锁之后自己再获取锁
(3)await():当前线程T加入条件等待队列 释放锁 park()当前线程
signal():当前线程T节点出条件等待队列 T节点加入AQS同步队列 unpark()T线程
一、Condition实现生产者消费者问题
class BoundedBuffer {
final Lock lock = new ReentrantLock();
// condition 依赖于 lock 来产生
final Condition notFull = lock.newCondition();
final Condition notEmpty = lock.newCondition(); final Object[] items = new Object[100];
int putptr, takeptr, count; // 生产
public void put(Object x) throws InterruptedException {
lock.lock();
try {
while (count == items.length)
notFull.await(); // 队列已满,等待,直到 not full 才能继续生产
items[putptr] = x;
if (++putptr == items.length) putptr = 0;
++count;
notEmpty.signal(); // 生产成功,队列已经 not empty 了,发个通知出去
} finally {
lock.unlock();
}
} // 消费
public Object take() throws InterruptedException {
lock.lock();
try {
while (count == 0)
notEmpty.await(); // 队列为空,等待,直到队列 not empty,才能继续消费
Object x = items[takeptr];
if (++takeptr == items.length) takeptr = 0;
--count;
notFull.signal(); // 被我消费掉一个,队列 not full 了,发个通知出去
return x;
} finally {
lock.unlock();
}
}
}
二、lock与condition关系
// 一个lock可以new多个条件
public static void main(String[] args) {
ReentrantLock lock = new ReentrantLock();
lock.lock();
Condition newCondition1 = lock.newCondition();
newCondition1.await();
newCondition1.signal();
Condition newCondition2 = lock.newCondition();
newCondition2.await();
newCondition2.signal();
lock.unlock();
} // ReentrantLock.newCondition()
public Condition newCondition() {
return sync.newCondition();
} // ReentrantLock.Sync.newCondition()
final ConditionObject newCondition() {
return new ConditionObject();
}
三、条件队列
每个Condition对象都包含着一个FIFO队列,该队列是Condition对象通知/等待功能的关键。在队列中每一个节点(使用的AQS的节点)都包含着一个线程引用,该线程就是在该Condition对象上等待的线程。
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
private transient Node firstWaiter; //头节点
private transient Node lastWaiter; //尾节点
public ConditionObject() {
} /**
* 通过addConditionWaiter()方法理解等待队列数据结构
* 将当前线程加入条件等待队列
* 1.Node就是AQS的Node
* 2.单向链表,通过nextWaiter连接
* 3.waitStatus==Node.CONDITION才能在等待队列中
*/
private Node addConditionWaiter() {
Node t = lastWaiter;
if (t != null && t.waitStatus != Node.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;
} /**
* 清除队列中不是等待状态的线程
*/
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null; // 用于保存前一个节点
while (t != null) {
Node next = t.nextWaiter;
if (t.waitStatus != Node.CONDITION) {
t.nextWaiter = null;
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}
}
四、等待await()
public final void await() throws InterruptedException {
if (Thread.interrupted())
throw new InterruptedException();
Node node = addConditionWaiter(); // 当前线程new Node()加入条件队列
int savedState = fullyRelease(node); // 释放当先线程的锁
int interruptMode = 0; /**
* 自旋:
* 1.当前节点不在同步队列(刚new的节点肯定不在),挂起当前线程,等待被唤醒
* 2.当其他线程调用同一个ConditionObject的signal方法时,会将队列里的节点放入同步队列,并unpark线程(排队唤醒)
* 3.如果该节点被唤醒,再自旋检查是否在同步队列。发现已经在队列中,就可以跳出循环,获取lock
*/
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);
}
五、唤醒signal()
public final void signal() {
if (!isHeldExclusively()) // 检查是否拿到锁
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
} private void doSignal(Node first) {
do {
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;
Node p = enq(node); // 加入同步队列
int ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread); // 唤醒线程
return true;
}
await() :造成当前线程在接到信号或被中断之前一直处于等待状态。
await(long time, TimeUnit unit) :造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。
awaitNanos(long nanosTimeout) :造成当前线程在接到信号、被中断或到达指定等待时间之前一直处于等待状态。返回值表示剩余时间,如果在nanosTimesout之前唤醒,那么返回值 = nanosTimeout – 消耗时间,如果返回值 <= 0 ,则可以认定它已经超时了。
awaitUninterruptibly() :造成当前线程在接到信号之前一直处于等待状态。【注意:该方法对中断不敏感】。
awaitUntil(Date deadline) :造成当前线程在接到信号、被中断或到达指定最后期限之前一直处于等待状态。如果没有到指定时间就被通知,则返回true,否则表示到了指定时间,返回返回false。
signal():唤醒一个等待线程。该线程从等待方法返回前必须获得与Condition相关的锁。
signal()All:唤醒所有等待线程。能够从等待方法返回的线程必须获得与Condition相关的锁。
参考资料 / 相关推荐
一行一行源码分析清楚 AbstractQueuedSynchronizer (二)
Java多线程系列--“JUC锁”06之 Condition条件
Java并发(十一):Condition条件的更多相关文章
- java 并发时使用条件变量--Condition
lock--unlock的方式在实际中使用较少,一般使用synchronized获取对象的内部锁替代,但是lock--unlock对了解synchronized有很大的帮助. 创建一个bank对象用于 ...
- Java并发——使用Condition线程间通信
线程间通信 线程之间除了同步互斥,还要考虑通信.在Java5之前我们的通信方式为:wait 和 notify.Condition的优势是支持多路等待,即可以定义多个Condition,每个condit ...
- 浅谈Java中的Condition条件队列,手摸手带你实现一个阻塞队列!
条件队列是什么?可能很多人和我一样答不出来,不过今天终于搞清楚了! 什么是条件队列 条件队列:当某个线程调用了wait方法,或者通过Condition对象调用了await相关方法,线程就会进入阻塞状态 ...
- Java ReEntrantLock 之 Condition条件(Java代码实战-002)
import java.util.LinkedList; import java.util.concurrent.locks.Condition; import java.util.concurren ...
- JAVA并发编程--Condition
Condition主要是为了在J.U.C框架中提供和Java传统的监视器风格的wait,notify和notifyAll方法类似的功能. AQS等待队列与Condition队列是两个相互独立的队列 a ...
- java并发:Condition的应用
Condition类可以使线程等待,也可以唤醒线程.Condition类的await方法和Object类的wait方法等效Condition类的signal方法和Object类的notify方法等效C ...
- Java并发:Condition接口
Condition 接口与 Lock 配合实现了等待 / 通知模式,这个和 Object 的监视器方法(wait.notify.notifyAll 等方法)一样,都是实现了等待 / 通知模式,但这两者 ...
- java并发多线程显式锁Condition条件简介分析与监视器 多线程下篇(四)
Lock接口提供了方法Condition newCondition();用于获取对应锁的条件,可以在这个条件对象上调用监视器方法 可以理解为,原本借助于synchronized关键字以及锁对象,配备了 ...
- 【Java并发系列04】线程锁synchronized和Lock和volatile和Condition
img { border: solid 1px } 一.前言 多线程怎么防止竞争资源,即防止对同一资源进行并发操作,那就是使用加锁机制.这是Java并发编程中必须要理解的一个知识点.其实使用起来还是比 ...
随机推荐
- 机器学习-kNN-寻找最好的超参数
一 .超参数和模型参数 超参数:在算法运行前需要决定的参数 模型参数:算法运行过程中学习的参数 - kNN算法没有模型参数- kNN算法中的k是典型的超参数 寻找好的超参数 领域知识 经验数值 实验搜 ...
- Linux必备工具Tmux
之前介绍了Linux的Screen命令,今天介绍一个更为强大的终端工具Tmux. Tmux 是一个用于在一个终端窗口中运行多个终端会话的工具.它基本能替代nohup以及screen,甚至比它们更为强大 ...
- L - Looking for Taste Gym - 101991L 二进制枚举/思维
方法一:因为最多是10的六次方,所以可以直接枚举二进制上的每一位来得到最优结果. AC代码: #include<iostream> #include<stack> #inclu ...
- SQL server(到主机的TCPIPl连接失败的问题)
1 首先要做的是在sql新建查询里输入 exec sys.sp_readerrorlog 0, 1, 'listening' 运行后 会显示你的sql 正在运行的tcp/ip接口 看看是否和你java ...
- GSON转换日期数据为特定的JSON数据
通过JSON传递数据的时候经常需要传递日期,Java中可以通过GSON将日期转换为特定格式的JSON数据. 1.普通的GSON转换日期 public void query(HttpServletReq ...
- Linux下搜索命令
linux下用于查找文件的5个命令,有需要的朋友可以参考下.包括find,whereis,locate,which与type. linux下用于查找文件的5个命令,有需要的朋友可以参考下.包括find ...
- shell source命令说明
当我修改了/etc/profile文件,我想让它立刻生效,而不用重新登录:这时就想到用source命令,如:source /etc/profile对source进行了学习,并且用它与sh 执行脚本进行 ...
- Django Authentication 用户认证系统
一. Django的认证系统 Django自带一个用户认证系统,用于处理用户账户.群组.许可和基于cookie的用户会话. 1.1 概览 Django的认证系统包含了身份验证和权限管理两部分.简单地说 ...
- rcnn ->fast rcnn->faster rcnn物体检测论文
faster rcnn中的rpn网络: 特征可以看做一个尺度51*39的256通道图像,对于该图像的每一个位置,考虑9个可能的候选窗口:三种面积{1282,2562,5122}×三种比例{1:1,1: ...
- caffe使用finetume
训练时, solver.prototxt中使用的是train_val.prototxt ./build/tools/caffe/train -solver ./models/bvlc_referenc ...