- 浏览: 738344 次
- 性别:
- 来自: 北京
文章分类
最新评论
-
nmgrd:
赞一个,在分析AQS源码的博客当中,楼主是写的最细致,最易懂, ...
java并发编程--AbstractQueuedSynchronizer公平锁和非公平锁分析(三) -
ljzxloaf:
阻塞和等待不一样吧,condition持有等待队列,而AQS持 ...
java并发编程--AbstractQueuedSynchronizer加锁和解锁分析(二) -
DaCang4535:
楼主,不知道你那个book类有什么作用。setBook那个方法 ...
JAXB--简单应用(一) -
yuhui0531:
f-u-c-k!!!!!!!!!!!!!!!!!!!!!!!! ...
java线程常见的几种死锁模式和解决方法 -
刘文超:
form提交的几种方法
前一篇J.U.C的锁的获取与释放的过程,这个过程主要通过在A.Q.S中维持一个等待队列来实现,其中我们也提到了,在A.Q.S中除了一个等待队列之外,还有多个Condition队列(Condition队列的多少取决于ConditionObject对象个数),在了解Condition队列之前,先来看一下Condition是怎么回事:
The synchronizer framework provides a ConditionObject class for use by synchronizers that maintain exclusive synchronization and conform to the Lock interface. Any number of condition objects may be attached to a lock object, providing classic monitor-style await, signal, and signalAll operations, including those with timeouts, along with some inspection and monitoring methods.
上面的这一段内容摘自Doug Lea的AQS论文,从上面这一段话可以看出,Condition主要是为了在J.U.C框架中提供和Java传统的监视器风格的wait,notify和notifyAll方法类似的功能,那么先来解释一下这三个方法的作用:
- Object.wait()方法:使当前线程释放Object上的监视器并且挂起,直到有另外的线程调用Object.notify()方法或者Object.notifyAll()方法唤醒当前线程,当被唤醒后,Object.wait()方法会尝试重新获取监视器,成功获取后继续往下执行。注意Object.wait()方法只有在当前线程持有Object的监视器的时候才能够调用,不然会抛出异常。
- Object.notify()方法:用于唤醒另外一个调用了Object.wait()方法的线程,如果有多个都调用了Object.wait()方法,那么就会选择一个线程去notify(),具体选择哪一个和具体的实现有关,当前线程在调用Object.notify()方法以后会就释放Object的监视器,和wait()方法一样,Object.notify()方法只有在当前线程只有Object的监视器的时候才能够调用,不然就会抛出异常。
- Object.notifyAll()方法:唤醒所有调用了Object.wait()方法的线程,如果有多个线程调用了Object.wait()方法,那么就会引发这些线程之间的竞争,最后谁成功获取到Object的监视器和具体的实现有关,当前线程在调用Object.notifyAll()方法以后会就释放Object的监视器,和wait()方法一样,Object.notifyAll()方法只有在当前线程只有Object的监视器的时候才能够调用,不然就会抛出异常。
那么Condition是如何实现wait,notify和notifyAll方法的功能呢?我们接下来看:
在Condition中,wait,notify和notifyAll方法分别对应了await,signal和signalAll方法,当然Condition也提供了超时的、不可被中断的await()方法,不过我们主要还是看一看await,notify和notifyAll的实现,先看await:
public final void await() throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); Node node = addConditionWaiter(); int savedState = fullyRelease(node); int interruptMode = 0; 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) unlinkCancelledWaiters(); if (interruptMode != 0) reportInterruptAfterWait(interruptMode); }
整个await的过程如下:
- 在第2行处,如果当前线程被中断,则抛出中断异常。
- 在第4行处,将节点加入到Condition队列中去,这里如果lastWaiter是cancel状态,那么会把它踢出Condition队列。
- 在第5行处,调用tryRelease,释放当前线程的锁
- 在第7行处,判断节点是否在等待队列中(signal操作会将Node从Condition队列中拿出并且放入到等待队列中去),如果不在等待队列中了,就park当前线程,如果在,就退出循环,这个时候如果被中断,那么就退出循环
- 在第12行处,这个时候线程已经被signal()或者signalAll()操作给唤醒了,退出了4中的while循环,尝试再次获取锁,调用acquireQueued方法。
可以看到,这个await的操作过程和Object.wait()方法是一样,只不过await()采用了Condition队列的方式实现了Object.wait()的功能。
signal和signalAll方法:
在了解了await方法的实现以后,signal和signalAll方法的实现就相对简单了,先看看signal方法:
public final void signal() { if (!isHeldExclusively()) throw new IllegalMonitorStateException(); Node first = firstWaiter; if (first != null) doSignal(first); }
这里先判断当前线程是否持有锁,如果没有持有,则抛出异常,然后判断整个condition队列是否为空,不为空则调用doSignal方法来唤醒线程,看看doSignal方法都干了一些什么:
private void doSignal(Node first) { do { if ( (firstWaiter = first.nextWaiter) == null) lastWaiter = null; first.nextWaiter = null; } while (!transferForSignal(first) && (first = firstWaiter) != null); }
这个while循环的作用就是将firstWaiter往Condition队列的后面移一位,并且唤醒first,看看while循环中tranferForSignal:
final boolean transferForSignal(Node node) { /* * If cannot change waitStatus, the node has been cancelled. */ if (!compareAndSetWaitStatus(node, Node.CONDITION, 0)) return false; /* * Splice onto queue and try to set waitStatus of predecessor to * indicate that thread is (probably) waiting. If cancelled or * attempt to set waitStatus fails, wake up to resync (in which * case the waitStatus can be transiently and harmlessly wrong). */ Node p = enq(node); int c = p.waitStatus; if (c > 0 || !compareAndSetWaitStatus(p, c, Node.SIGNAL)) LockSupport.unpark(node.thread); return true; }
这段代码的作用就是修改Node的waitStatus为0,然后将Node插入到等待队列中,并且唤醒Node。
signalAll和signal方法类似,主要的不同在于它不是调用doSignal方法,而是调用doSignalAll方法:
private void doSignalAll(Node first) { lastWaiter = firstWaiter = null; do { Node next = first.nextWaiter; first.nextWaiter = null; transferForSignal(first); first = next; } while (first != null); }
这个方法就相当于把Condition队列中的所有Node全部取出插入到等待队列中去。
注(原文章中图和文字有些出入,在此做些标注,以备查看):
1.执行Condition.await()方法的一般都是获得锁的当前线程,该线程应该不在等待队列中。
2.执行Condition.await()方法后,addConditionWaiter()方法会首先把当前线程包装成Node结点后,添加到等待队列中。而不是首先将等待队列的head往后移。
3.等待队列若不为空,一般head结点都指向一个哑元(dummy node),head->next才是真正被阻塞的线程。
转载自: http://www.goldendoc.org/2011/06/juc_condition/ Java并发编程J.U.C之Condition
发表评论
-
thread方法分析
2012-02-02 13:36 0t.join();表示当前线程停止执行直到t线程运行完毕 ... -
线程池--jetty中QueuedThreadPool分析(一)
2012-02-02 11:09 15533jetty版本:jetty-6.1.26 1.由于jetty ... -
线程池--jetty中QueuedThreadPool分析(一)
2012-02-02 10:48 0d -
java并发编程--ThreadLocal的用法和分析
2012-01-11 22:55 17571ThreadLocal这个类,理解起来比较简单,但是使用时, ... -
java并发编程--Semaphore信号量分析(七)
2012-01-08 22:25 0Mutex是一把钥匙,一个人拿了就可进入一个房间,出来的时候把 ... -
java并发编程--AbstractQueuedSynchronizer的tryLock()方法分析(六)
2012-01-05 14:40 2277tryLock()仅尝试一次获取锁,不管成功与否,都将返回结 ... -
java并发编程--AbstractQueuedSynchronizer的lock()和lockInterruptibly()方法分析(五)
2011-12-31 11:19 5771lock 与 lockInterruptibly比较区别 ... -
java并发编程--AbstractQueuedSynchronizer公平锁和非公平锁分析(三)
2011-12-30 14:40 3866juc包中,aqs实现的公平锁和非公平锁的最主要的区别是:非 ... -
java并发编程--AbstractQueuedSynchronizer加锁和解锁分析(二)
2011-12-29 22:07 8500在java.util.concurrent.locks ... -
JAXB--@XmlType注解标注xml生成顺序(四)
2011-11-03 17:01 34365默认情况下,Jaxb编组出来的xml中的字段顺序是随机的,你可 ... -
JAXB--@XmlElementWrapper注解和泛型一起使用(三)
2011-11-03 16:46 20695当java对象的某个属性使 ... -
JAXB--@XmlElementWrapper注解(二)
2011-11-03 15:49 32357在JAXB标准中,@XmlElementWrapper注解表示 ... -
JAXB--简单应用(一)
2011-11-03 15:09 50397一、简介 1、概念是什么:(Java Archite ... -
Java线程:新特征-锁
2011-10-28 17:39 0在Java5中,专门提供了锁对象,利用锁可以方便的实现资源的 ... -
java线程常见的几种死锁模式和解决方法
2011-10-18 23:09 2161待续 -
利用object.wait()和实现生产者和消费者模式
2011-10-18 23:08 2557其实生产者和消费者模式概念很简单,就是生产者将生产出来 ... -
java线程的同步互斥和通讯
2011-10-18 23:01 3602一、同步和锁定 1、锁的原理 Ja ... -
HtmlParser学习笔记(四)-- 使用Filter过滤结点
2010-01-12 12:48 11209使用TagNameFilter过滤所有table标签结 ... -
HtmlParser学习笔记(三)-- 使用Visitor访问结点
2010-01-12 12:48 5411使用NodeVisitor方式访问html结点,代码如 ... -
HtmlParser学习笔记(二)-- 遍历结点
2010-01-12 12:47 4331这次主要演示下,如何迭代一个Node结点的所有根子结点。 ...
相关推荐
AbstractQueuedSynchronizer(AQS)详解.mp4 使用AQS重写自己的锁.mp4 重入锁原理与演示.mp4 读写锁认识与原理.mp4 细读ReentrantReadWriteLock源码.mp4 ReentrantReadWriteLock锁降级详解.mp4 线程安全性问题简单总结...
龙果 java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四...
Java 并发编程中的 JUC(java.util.concurrent)库以及其核心组件 AQS(AbstractQueuedSynchronizer)在构建高性能、可伸缩性的多线程应用方面具有重要的地位。 AQS 是 JUC 中的核心组件,它提供了一个框架,让...
Java并发编程实战 本书深入浅出地介绍了Java线程和并发,是一本完美的Java并发参考手册。书中从并发性和线程安全性的基本概念出发,介绍了如何使用类库提供的基本并发构建块,用于避免并发危险、构造线程安全的类及...
第4节学习并发的四个阶段并推荐学习并发的资料 [免费观看] 00:09:13分钟 | 第5节线程的状态以及各状态之间的转换详解00:21:56分钟 | 第6节线程的初始化,中断以及其源码讲解00:21:26分钟 | 第7节多种创建线程的...
java并发编程原理实战 第2节理解多线程与并发的之间的联系与区别 [免费观看] 00:11:59分钟 | 第3节解析多线程与多进程的联系以及上下文切换所导致资源浪费问题 [免费观看] 00:13:03分钟 | 第4节学习并发的四个...
第4节学习并发的四个阶段并推荐学习并发的资料 [免费观看] 00:09:13分钟 | 第5节线程的状态以及各状态之间的转换详解00:21:56分钟 | 第6节线程的初始化,中断以及其源码讲解00:21:26分钟 | 第7节多种创建线程的...
Java 并发编程在现代软件开发中占据重要地位,尤其是在多核处理器的时代。JUC(java.util.concurrent)库是 Java 标准库的一部分,提供了丰富的多线程并发工具,旨在帮助开发者编写高性能、高可伸缩性的并发程序。...
1.并发编程的优缺点 2.线程的状态转换以及基本操作 3.java内存模型以及happens-before规则 4.彻底理解synchronized 5.彻底理解volatile 6.你以为你真的了解final吗? 7.三大性质总结:原子性、可见性以及有序性 8....
之所以把这一章节叫做AQS简介而不是叫AQS详解,是因为已经有大神写过详解的文章Java并发之AQS详解,这篇文章对AQS的源码解析很透彻,博主读了之后受益匪浅,鉴于对原作者的尊重,所以如上附上原文的链接。...
Java并发包源码分析(JDK1.8):囊括了java.util.concurrent包中大部分类的源码分析,其中涉及automic包,locks包(AbstractQueuedSynchronizer、ReentrantLock、ReentrantReadWriteLock、LockSupport等),queue...
从并发概念、场景分析出发,依次引出锁、等待队列等概念,直至分析清楚java锁机制实现的原理。并以java锁机制实现基类AbstractQueuedSynchronizer的实现为例,从类(核心属性、方法)设计思路,到对关键代码做注释...