Java 并发编程-再谈 AbstractQueuedSynchronizer 2:共享模式与基于 Condition 的等待 / 通知机制实现
2017-08-29 15:38
871 查看
共享模式acquire实现流程
上文我们讲解了AbstractQueuedSynchronizer独占模式的acquire实现流程,本文趁热打铁继续看一下AbstractQueuedSynchronizer共享模式acquire的实现流程。连续两篇文章的学习,也可以对比独占模式acquire和共享模式acquire的区别,加深对于AbstractQueuedSynchronizer的理解。先看一下共享模式acquire的实现,方法为acquireShared和acquireSharedInterruptibly,两者差别不大,区别就在于后者有中断处理,以acquireShared为例:
addWaiter,把所有tryAcquireShared<0的线程实例化出一个Node,构建为一个FIFO队列,这和独占锁是一样的
拿当前节点的前驱节点,只有前驱节点是head的节点才能tryAcquireShared,这和独占锁也是一样的
前驱节点不是head的,执行”shouldParkAfterFailedAcquire() && parkAndCheckInterrupt()”,for(;;)循环,”shouldParkAfterFailedAcquire()”方法执行2次,当前线程阻塞,这和独占锁也是一样的
确实,共享模式下的acquire和独占模式下的acquire大部分逻辑差不多,最大的差别在于tryAcquireShared成功之后,独占模式的acquire是直接将当前节点设置为head节点即可,共享模式会执行setHeadAndPropagate方法,顾名思义,即在设置head之后多执行了一步propagate操作。setHeadAndPropagate方法源码为:
第19行~第23行的代码是独占锁和共享锁最不一样的一个地方,我们再看独占锁acquireQueued的代码:
共享模式release实现流程
上面讲了共享模式下acquire是如何实现的,下面再看一下release的实现流程,方法为releaseShared:头结点本身的waitStatus是SIGNAL且能通过CAS算法将头结点的waitStatus从SIGNAL设置为0,唤醒头结点的后继节点
头结点本身的waitStatus是0的话,尝试将其设置为PROPAGATE状态的,意味着共享状态可以向后传播
Condition的await()方法实现原理—-构建等待队列
我们知道,Condition是用于实现通知/等待机制的,和Object的wait()/notify()一样,由于本文之前描述AbstractQueuedSynchronizer的共享模式的篇幅不是很长,加之Condition也是AbstractQueuedSynchronizer的一部分,因此将Condition也放在这里写了。Condition分为await()和signal()两部分,前者用于等待、后者用于唤醒,首先看一下await()是如何实现的。Condition本身是一个接口,其在AbstractQueuedSynchronizer中的实现为ConditionObject:
像ReentrantLock每次要使用ConditionObject,直接new一个ConditionObject出来即可。我们关注一下await()方法的实现:
那么unlinkCancelledWaiters做了什么?里面的流程就不看了,就是一些指针遍历并判断状态的操作,总结一下就是:从头到尾遍历每一个Node,遇到Node的waitStatus不是CONDITION的就从队列中踢掉,该节点的前后节点相连。
接着第8行的代码前面说过了,new出来了一个Node,存储了当前线程,waitStatus是CONDITION,接着第9行~第13行的操作很好理解:
如果lastWaiter是null,说明FIFO队列中没有任何Node,firstWaiter=Node
如果lastWaiter不是null,说明FIFO队列中有Node,原lastWaiter的next指向Node
无论如何,新加入的Node编程lastWaiter,即新加入的Node一定是在最后面
用一张图表示一下构建的数据结构就是:
对比学习,我们总结一下Condition构建出来的队列和AbstractQueuedSynchronizer构建出来的队列的差别,主要体现在2点上:
AbstractQueuedSynchronizer构建出来的队列,头节点是一个没有Thread的空节点,其标识作用,而Condition构建出来的队列,头节点就是真正等待的节点
AbstractQueuedSynchronizer构建出来的队列,节点之间有next与pred相互标识该节点的前一个节点与后一个节点的地址,而Condition构建出来的队列,只使用了nextWaiter标识下一个等待节点的地址
整个过程中,我们看到没有使用任何CAS操作,firstWaiter和lastWaiter也没有用volatile修饰,其实原因很简单:要await()必然要先lock(),既然lock()了就表示没有竞争,没有竞争自然也没必要使用volatile+CAS的机制去保证什么。
Condition的await()方法实现原理—-线程等待
前面我们看了Condition构建等待队列的过程,接下来我们看一下等待的过程,await()方法的代码比较短,再贴一下:接着看await()方法的第7行判断”while(!isOnSyncQueue(node))”:
至此调用await()方法的线程构建Condition等待队列–释放锁–等待的过程已经全部分析完毕。
Condition的signal()实现原理
上面的代码分析了构建Condition等待队列–释放锁–等待的过程,接着看一下signal()方法通知是如何实现的:那么真正操作的时候,获取第一个waiter,如果有waiter,调用doSignal方法:
重新设置firstWaiter,指向第一个waiter的nextWaiter
如果第一个waiter的nextWaiter为null,说明当前队列中只有一个waiter,lastWaiter置空
因为firstWaiter是要被signal的,因此它没什么用了,nextWaiter置空
接着执行第6行和第7行的代码,这里重点就是第6行的transferForSignal方法:
尝试将Node的waitStatus从CONDITION置为0,这一步失败直接返回false
当前节点进入调用enq方法进入AbstractQueuedSynchronizer队列
当前节点通过CAS机制将waitStatus置为SIGNAL
最后上面的步骤全部成功,返回true,返回true唤醒等待节点成功。从唤醒的代码我们可以得出一个重要结论:某个await()的节点被唤醒之后并不意味着它后面的代码会立即执行,它会被加入到AbstractQueuedSynchronizer队列的尾部,只有前面等待的节点获取锁全部完毕才能轮到它。
代码分析到这里,我想类似的signalAll方法也没有必要再分析了,显然signalAll方法的作用就是将所有Condition队列中等待的节点逐一队列中从移除,由CONDITION状态变为SIGNAL状态并加入AbstractQueuedSynchronizer队列的尾部。
代码示例
可能大家看了我分析半天代码会有点迷糊,这里最后我贴一段我用于验证上面Condition结论的示例代码,首先建立一个Thread,我将之命名为ConditionThread:线程1先启动,获取锁,调用await()方法等待
线程0后启动,获取锁,休眠5秒准备signal()
线程2最后启动,获取锁,由于线程0未使用完毕锁,因此线程2排队,可以此时由于线程0还未signal(),因此线程1在线程0执行signal()后,在AbstractQueuedSynchronizer队列中的顺序是在线程2后面的
代码执行结果为:
相关文章推荐
- 再谈AbstractQueuedSynchronizer2:共享模式与基于Condition的等待/通知机制实现
- java基于AbstractQueuedSynchronizer实现资源共享锁,限制并发线程数目
- 再谈AbstractQueuedSynchronizer2:共享模式与基于Condition的等待/通知机制实现
- Java 并发编程-再谈 AbstractQueuedSynchronizer 3 :基于 AbstractQueuedSynchronizer 的并发类实现
- Java 并发 ---AbstractQueuedSynchronizer-共享模式与Condition
- Java并发系列之AbstractQueuedSynchronizer源码分析(共享模式)
- 深入学习java并发编程:Lock与AbstractQueuedSynchronizer(AQS)实现
- Java并发编程-AbstractQueuedSynchronizer源码分析
- java并发编程--AbstractQueuedSynchronizer加锁和解锁分析
- Java并发系列之AbstractQueuedSynchronizer源码分析(独占模式)
- java并发编程--AbstractQueuedSynchronizer的tryLock()方法分析(六)
- AbstractQueuedSynchronizer同步队列与Condition等待队列协同机制
- java AbstractQueuedSynchronizer的实现分析(共享锁)
- Java并发:AbstractQueuedSynchronizer详解(独占模式)
- Java并发编程:AbstractQueuedSynchronizer的内部结构
- AbstractQueuedSynchronizer同步队列与Condition等待队列协同机制
- Java并发源码剖析(一)——AbstractQueuedSynchronizer独占模式
- 【Java】关于基于并发类AbstractQueuedSynchronizer的实现研究
- Java 并发 ---AbstractQueuedSynchronizer(同步器)-独占模式
- java并发编程--AbstractQueuedSynchronizer的lock()和lockInterruptibly()方法分析(五)