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并发编程中必须要理解的一个知识点.其实使用起来还是比 ...
随机推荐
- NYOJ 129 树的判定 (并查集)
题目链接 描述 A tree is a well-known data structure that is either empty (null, void, nothing) or is a set ...
- 【译】第五篇 SQL Server代理理解代理错误日志
本篇文章是SQL Server代理系列的第五篇,详细内容请参考原文. 正如这一系列的前几篇所述,SQL Server代理作业是由一系列的作业步骤组成,每个步骤由一个独立的类型去执行.在第四篇中我们看到 ...
- javashop每次重新部署都要从新安装的问题
javashop每次重新部署都要从新安装的问题 发现一个问题就是用MyEclipse是部署不上的,用eclipse才行. 这个问题的关键在于javashop有好多文件都是动态生成的,好多配置文件也是在 ...
- 阿里分布式开源框架DUBBO 入门+ 进阶+ 项目实战视频教程
史诗级Java/JavaWeb学习资源免费分享 欢迎关注我的微信公众号:"Java面试通关手册"(坚持原创,分享各种Java学习资源,面试题,优质文章,以及企业级Java实战项目回 ...
- think php模板的使用
{include file="../application/public/header.html"}<!-- Jumbotron --><div class=&q ...
- ps aux排序
按内存升序排列: ps aux --sort=+rss 按内存降序排列: ps aux --sort=-rss 按cpu升序排列: ps aux --sort=+%cpu 为按cpu降序排列. ps ...
- Centos 6.4搭建git服务器【转】
前阵子公司需要,让我搭个Git服务器,把之前用的SVN上代码迁移到git上去,所以就在阿里云主机上搭了一个,记录了下安装过程,留存文档以备查阅.本篇本章只涉及搭建部分的操作,更多git的使用可以参考文 ...
- 前端nginx时,让后端tomcat记录真实IP【转】
对于nginx+tomcat这种架构,如果后端tomcat配置保持默认,那么tomcat的访问日志里,记录的就是前端nginx的IP地址,而不是真实的访问IP.因此,需要对nginx.tomcat做如 ...
- Linux搭建svn服务
svn是为了方便代码进行版本控制 Linux)svn服务器 --> windows) svn访问端 ********* [root@svn ~]# yum install -y subversi ...
- C++ 和Java继承机制的比较
摘要: C++支持类的多继承,而Java采用类的单继承.C++中的继承成分只有类(模板属于带参数的类,结构和联合是特殊的类),Java中除了类还有接口的继承,而且允许接口的多继承,可以间接地实现类多继 ...