Condition主要是为了在J.U.C框架中提供和Java传统的监视器风格的wait,notify和notifyAll方法类似的功能。

AQS等待队列与Condition队列是两个相互独立的队列

await()就是在当前线程持有锁的基础上释放锁资源,并新建Condition节点加入到Condition的队列尾部,阻塞当前线程
signal()就是将Condition的头节点移动到AQS等待节点尾部,让其等待再次获取锁
 

线程何时阻塞和释放

阻塞:await()方法中,在线程释放锁资源之后,如果节点不在AQS等待队列,则阻塞当前线程,如果在等待队列,则自旋等待尝试获取锁
释放:signal()后,节点会从condition队列移动到AQS等待队列,则进入正常锁的获取流程
 

await方法

public final void await() throws InterruptedException {
// 如果当前线程被中断,则抛出中断异常
if (Thread.interrupted())
throw new InterruptedException();
// 将节点加入到Condition队列中去,这里如果lastWaiter是cancel状态,那么会把它踢出Condition队列。
Node node = addConditionWaiter();
// 调用tryRelease,释放当前线程的锁
long savedState = fullyRelease(node);
int interruptMode = 0;
// 为什么会有在AQS的等待队列的判断?
// 解答:signal操作会将Node从Condition队列中拿出并且放入到等待队列中去,在不在AQS等待队列就看signal是否执行了
// 如果不在AQS等待队列中,就park当前线程,如果在,就退出循环,这个时候如果被中断,那么就退出循环
while (!isOnSyncQueue(node)) {
LockSupport.park(this);
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
// 这个时候线程已经被signal()或者signalAll()操作给唤醒了,退出了4中的while循环
// 自旋等待尝试再次获取锁,调用acquireQueued方法
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
if (node.nextWaiter != null)
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
整个await的过程如下:
  1.将当前线程加入Condition锁队列。
  2.释放锁(fullyRelease)。这里可以看到将锁释放了,否则别的线程就无法拿到锁而发生死锁。进行3。
  3.自旋(while), 如果不在AQS等待队列,则挂起自己,直到被唤醒或者超时或者CACELLED等。进行4。
  4.获取锁(acquireQueued)。并将自己从Condition的FIFO队列中释放,表明自己不再需要锁(我已经拿到锁了)。
可以看到,这个await的操作过程和Object.wait()方法是一样,只不过await()采用了Condition队列的方式实现了Object.wait()的功能。

signal方法

signal就是唤醒Condition队列中的第一个非CANCELLED节点线程,而signalAll就是唤醒所有非CANCELLED节点线程。当然了遇到CANCELLED线程就需要将其从FIFO队列中剔除。

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) {
/*
* 设置node的waitStatus:Condition->0
*/
if (!compareAndSetWaitStatus(node, Node.CONDITION, 0))
return false; /*
* 加入到AQS的等待队列,让节点继续获取锁
* 设置前置节点状态为SIGNAL
*/
Node p = enq(node);
int c = p.waitStatus;
if (c > 0 || !compareAndSetWaitStatus(p, c, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}

JAVA并发编程--Condition的更多相关文章

  1. Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  2. java并发编程——通过ReentrantLock,Condition实现银行存取款

         java.util.concurrent.locks包为锁和等待条件提供一个框架的接口和类,它不同于内置同步和监视器.该框架允许更灵活地使用锁和条件,但以更难用的语法为代价. Lock 接口 ...

  3. Java并发编程原理与实战二十二:Condition的使用

    Condition的使用 Condition用于实现条件锁,可以唤醒指定的阻塞线程.下面来实现一个多线程顺序打印a,b,c的例子. 先来看用wait和notify的实现: public class D ...

  4. 19、Java并发编程:线程间协作的两种方式:wait、notify、notifyAll和Condition

    Java并发编程:线程间协作的两种方式:wait.notify.notifyAll和Condition 在前面我们将了很多关于同步的问题,然而在现实中,需要线程之间的协作.比如说最经典的生产者-消费者 ...

  5. 006 Java并发编程wait、notify、notifyAll和Condition

    原文https://www.cnblogs.com/dolphin0520/p/3920385.html#4182690 Java并发编程:线程间协作的两种方式:wait.notify.notifyA ...

  6. 【java并发编程】Lock & Condition 协调同步生产消费

    一.协调生产/消费的需求 本文内容主要想向大家介绍一下Lock结合Condition的使用方法,为了更好的理解Lock锁与Condition锁信号,我们来手写一个ArrayBlockingQueue. ...

  7. 【Java并发编程实战】----- AQS(四):CLH同步队列

    在[Java并发编程实战]-–"J.U.C":CLH队列锁提过,AQS里面的CLH队列是CLH同步锁的一种变形.其主要从两方面进行了改造:节点的结构与节点等待机制.在结构上引入了头 ...

  8. 【Java并发编程实战】-----“J.U.C”:CyclicBarrier

    在上篇博客([Java并发编程实战]-----"J.U.C":Semaphore)中,LZ介绍了Semaphore,下面LZ介绍CyclicBarrier.在JDK API中是这么 ...

  9. 【Java并发编程实战】-----“J.U.C”:ReentrantReadWriteLock

    ReentrantLock实现了标准的互斥操作,也就是说在某一时刻只有有一个线程持有锁.ReentrantLock采用这种独占的保守锁直接,在一定程度上减低了吞吐量.在这种情况下任何的"读/ ...

随机推荐

  1. Java数据结构——顺序表

    一个线性表是由n(n≥0)个数据元素所构成的有限序列. 线性表逻辑地表示为:(a0,a1,…,an-1).其中,n为线性表的长度,n=0时为空表.i为ai在线性表中的位序号. 存储结构:1.顺序存储, ...

  2. Docker 学习笔记(二)

    进入当前正在运行的容器 # 我们通常容器都是使用后台方式运行的,需要进入容器,修改一些配置 # 命令: docker exec -it 容器 id bashshell 测试 我们通常容器都是使用后台方 ...

  3. 用python导入20个G的json数据到Mysql数据库

    整体思路参考资料:https://blog.csdn.net/layman2016/article/details/79252499 作业:有一个16个G的跟疫情相关的json新闻大数据(articl ...

  4. vue-cli根据不同的命令自动切换不同环境地址

    前言 我们再使用vue脚手架开发项目时,不可避免的涉及到多个环境来回切换接口调用地址的问题,在开发环境中可能会通过 ip 来访问后台接口,但是当项目上线后就要把对用的接口地址换成生产环境的地址,肯定不 ...

  5. unity 真机调试

    [Unity3D]Android和ios真机调试测Profiler http://blog.csdn.net/swj524152416/article/details/53466413 Unity5. ...

  6. IDEA 左侧出现对勾,该如何去掉对勾呢?

    如下面 解决办法如下 单击按F11 或者ctrl +鼠标左键点击那个对串就可以决你的问题 有对勾是因为你把他添加进去了书签,方便下次自己看 我们可以在这个地方看到自己的书签也就是打对勾的地方

  7. 初学WebGL引擎-BabylonJS:第2篇-基础模型体验

    此次学习进度会比之前快很多,有了合适的学习方法后也就会有更多的乐趣产生了. 接上一章代码 上章代码 <!DOCTYPE html> <html> <head> &l ...

  8. iOS 报错: linker command failed with exit code 1 (use -v to see invocation) 原因

    在iOS开发中,很多人会遇到这样的报错 linker command failed with exit code 1 (use -v to see invocation) 可能的原因如下: 1.引用出 ...

  9. Copy a Xaml object

    <Control.Resources> <Button Click="Button_OnClick" x:Key="MyButton"> ...

  10. Unity图文混排

    http://blog.csdn.net/akof1314/article/details/49028279 http://blog.csdn.net/akof1314/article/details ...