描述
任意一个Java对象;都有一组监视器函数(定义在Object上),如wait()、wait(long timeout)、notify()以及notifyAll(),这些函数与synchronized同步关键字配合实现等待/通知模式。
Condition接口也提供了类似Object的监视器函数与Lock配合可以实现等待/通知模式,但是这两者在使用方式以及功能特性上还是有差别的。注意:只能独占锁才能使用Condition。
Condition接口的默认实现为AQS(AbstractQueuedSynchronizer)中的ConditionObject.
以ReentrantLock锁实现为示例,下面来看看Condition的实现与使用。
Condition使用DEMO
//一个阻塞队列中使用Condition的示例代码。
class ArrayBlockingQueue{
//保护所有操作的主锁
final ReentrantLock lock;
//poll操作的等待条件,队列为空时等待
private final Condition notEmpty;
//offer操作的等待条件,队列为满时等待
private final Condition notFull;
//生成实现并构建需要使用到的Condition.
public ArrayBlockingQueue(int capacity) {
items = new int[capacity];
lock = new ReentrantLock(false);
notEmpty = lock.newCondition();
notFull = lock.newCondition();
}
//入队列,要使用Condition需要先加锁,然后使用Condition
public boolean offer(int value) throws InterruptedException {
//等待超时时间
long nanos = TimeUnit.SECONDS.toNanos(3);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
CAS自旋,等待notFull的Condition可以使用(有超时判断,超过时间会响应中断).
while (count == items.length) {
if (nanos <= 0) {
return false;
}
nanos = notFull.awaitNanos(nanos);
}
//执行入队列操作,到此步骤时表示队列可插入数据.
enqueue(value);
//唤醒一个等待notEmpty的Condition线程.
notEmpty.signal();
return true;
}finally {
lock.unlock();
}
}
//有阻塞等待的poll操作,如果队列为空,等待3S后重试,(有超时判断,超过时间会响应中断).
public int poll() throws InterruptedException {
//等待超时时间
long nanos = TimeUnit.SECONDS.toNanos(3);
final ReentrantLock lock = this.lock;
lock.lockInterruptibly();
try {
while (count == 0) {
if (nanos <= 0) {
return Integer.MIN_VALUE;
}
nanos = notEmpty.awaitNanos(nanos);
}
//执行出队操作,到此步骤表示队列中有数据可以取.
int value = dequeue();
//唤醒一个等待notFull的Condition线程.
notFull.signal();
return value;
}finally {
lock.unlock();
}
}
}
Condition实现
0,Condition结构
在AQS的ConditionObject实现中,维护有一个等待队列,并记录有队列的头尾节点.
//ConditionObject结构定义。
public class ConditionObject implements Condition, Serializable {
/** First node of condition queue. */
private transient Node firstWaiter; //链表头部节点
/** Last node of condition queue. */
private transient Node lastWaiter;//链表尾部节点
public ConditionObject() { }
....
}
1,获取Condition实例
在ReentrantLock中通过newCondition获取一个Condition的实例。
//ReentrantLock中获取一个Condition的实例的实现。
final ConditionObject newCondition() {
//直接new 一个AQS中的ConditionObject实例.
return new ConditionObject();
}
2,await
await让线程等待分为两种情况,第一种是不带超时时间,第二种是带超时时间。
await不带超时时间:
执行wait操作需要检查线程中断状态,如果线程中间,需要抛出异常,交给线程本身处理。
把线程添加到Condition的等待队列中(注意:不是锁的等待队列),并释放当前线程的锁资源,得到在释放锁资源以前原来锁的重入次数。
如果线程不在锁的等待队列中时(或不是队顶元素线程被signal唤醒会把线程添加到锁的等待队列中),让线程处于BLOCKED状态,当线程被signal唤醒后(此时线程会进入锁的等待队列,waitStatus被修改为0),并根据线程是否中断来考虑是否需要重新压入线程到锁的等待队列(线程中断需要压入到锁的等待队列)。
当线程被唤醒后,会通过“acquireQueued”尝试让线程获取锁,并判断线程是否中断(根据中断是否成功入队列来考虑是否需要重新设置线程的中断状态)
如果线程有后继节点,清理Condition等待队列中被取消的线程。
//ConditionObject.await,实现Condition的等待。
public final void await() throws InterruptedException {
//如果当前线程处于中断状态,直接抛出中断信号,通知线程处理.
if (Thread.interrupted())
throw new InterruptedException();
//把线程添加到等待队列尾部,线程节点的waitStatus状态为“CONDITION”.
//==>每次添加线程到队列时,都会清理掉队列中非"CONDITION"的节点.
//==>注意:这里的队列是Condition的等待队列,不是锁对应的等待队列.
Node node = addConditionWaiter();
//释放当前锁资源(让线程等待),并唤醒一个等待队列中的线程,
//==>函数返回值是锁的重入次数(state),在唤醒时线程时会重新压入此state值.
int savedState = fullyRelease(node);
int interruptMode = 0;
//如果节点(CONDITION)有后继节点或者在锁队列中,返回true.
//首次执行时,线程还未添加到锁的队列中,原则上此时返回false.
//只有线程被signal唤醒或中断,isOnSyncQueue才返回true.
while (!isOnSyncQueue(node)) {
//让线程进行BLOCKED状态,如果线程处于中断状态把当前线程添加到锁的等待队列中,
LockSupport.park(this);
//这里在线程唤醒后执行,如果线程处于中断状态,结束迭代.
//==>checkInterruptWhileWaiting返回值0线程未中断,什么都不做.
//====>线程如果处于中断状态:
//======>返回值"-1"表示添加锁等待队列成功,并成功设置waitStatus设置为0.
//======>返回值"1"表示添加锁等待队列失败,此时说明线程是处于唤醒状态,
//=========>"Thread.yield"让出cpu执行时间.
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//尝试让当前线程获取锁(在signal时会唤醒线程并添加到锁的等待队列).
//如果线程成功获取锁,锁的state会重新设置为savedState,否则BLOCKED线程等待前继节点唤醒.
//acquireQueued函数返回true表示线程被中断.
//==>acquireQueued返回true,
//====>"interruptMode != THROW_IE",说明线程中断重新入锁队列失败.
if (acquireQueued(node, savedState) && interruptMode != THROW_IE) {
//此时表示线程被中断,同时线程重入锁的等待队列失败,此时说明线程需要重入中断.
interruptMode = REINTERRUPT;
}
//如果线程有后继节点,清理Condition等待队列中被取消的线程.
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();
//根据中断是否重入(线程不在锁等待队列中,表示可重入),处理中断信号.
//==>不可重入时,抛出线程中断异常,否则重新设置线程的中断信号.
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
await带超时时间:
带超时时间的await与不带超时的await不同处在于当达到指定超时时间后,线程会自动唤醒,而不是等待signal信号来唤醒,其它流程与await相同。
//ConditionObject.await,实现Condition的等待,带超时时间。
public final boolean await(long time, TimeUnit unit)
throws InterruptedException {
long nanosTimeout = unit.toNanos(time);
//如果处理wait时,线程是中断状态,直接抛出异常,交由线程自己处理。
if (Thread.interrupted())
throw new InterruptedException();
//把当前线程添加到Condition的等待队列中.
Node node = addConditionWaiter();
//释放当前线程的锁资源,并得到当前锁的重入次数.
int savedState = fullyRelease(node);
final long deadline = System.nanoTime() + nanosTimeout;
boolean timedout = false;
int interruptMode = 0;
//如果节点(CONDITION)有后继节点或者在锁队列中,返回true.
//首次执行时,线程还未添加到锁的队列中,原则上此时返回false.
//只有线程被signal唤醒或中断,isOnSyncQueue才返回true.
while (!isOnSyncQueue(node)) {
//如果等待时间已经超过指定的时间线程被取消,
//=>transferAfterCancelledWait函数
//==>把线程添加到同步队列中(锁等待队列)结束当前的while迭代.
//==>如果超时取消发生在signal前(说明添加同步队列成功)返回true,
//====>添加同步队列成功,waitStatus的值为0
//==>如果signal发生成超时取消前,"Thread.yield"让出CPU执行时间,返回false.
if (nanosTimeout <= 0L) {
timedout = transferAfterCancelledWait(node);
break;
}
//如果设置的超时时间超过1秒,直接使用LockSupport.parkNanos等待指定时间.
//===>这种方法比CAS自旋更快(JDK文档说的)。
if (nanosTimeout >= spinForTimeoutThreshold)
LockSupport.parkNanos(this, nanosTimeout);
//判断线程是否被中断,如果中断:尝试添加线程到同步队列.
//==>(成功添加同步队列会设置waitStatus为0).
//"interruptMode!=0"表示线程处于中断状态,成功添加到同步队列为-1,否则为1.
//==>如果线程处于中断状态,结束当前迭代.
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
nanosTimeout = deadline - System.nanoTime();
}
//当线程被唤醒或者线程被中断,尝试获取线程锁,state会重新设置为savedState.
//acquireQueued返回true表示线程被中断(请参考AQS.acquireQueued).
//====>"interruptMode != THROW_IE",说明线程中断重新入锁队列失败.
if (acquireQueued(node, savedState) && interruptMode != THROW_IE) {
//此时表示线程被中断,同时线程重入锁的等待队列失败,此时说明线程需要重入中断.
interruptMode = REINTERRUPT;
}
//如果线程有后继节点,清理Condition等待队列中被取消的线程.
if (node.nextWaiter != null)
unlinkCancelledWaiters();
//根据中断是否重入(线程不在锁等待队列中,表示可重入),处理中断信号.
//==>不可重入时,抛出线程中断异常,否则重新设置线程的中断信号.
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
//返回true说明未超时,否则表示超时.
return !timedout;
}
addConditionWaiter
添加线程到Condition的等待队列,此函数只会在await操作时会调用,向队列尾部添加当前线程节点。
//把当前线程添加到Condition的等待队列中。
//注意:Condition的队列操作并没有使用原子操作,而是直接赋值操作(因为只有当前线程拥有锁)
private Node addConditionWaiter() {
Node t = lastWaiter;//lastWaiter是队列尾部节点
// If lastWaiter is cancelled, clean out.
//如果lastWaiter已经被取消,清理队列中已经取消的节点.
if (t != null && t.waitStatus != Node.CONDITION) {
unlinkCancelledWaiters();
//重新刷新lastWaiter的值.
t = lastWaiter;
}
//包装当前线程为一个CONDITION节点.
Node node = new Node(Thread.currentThread(), Node.CONDITION);
//"t == null"表示队列还未初始化,设置队列的头节点为当前线程。
//==>否则设置当前线程为lastWaiter的后继节点,并更新lastWaiter.
if (t == null)
firstWaiter = node;
else
t.nextWaiter = node;
lastWaiter = node;
return node;
}
fullyRelease
释放线程的锁资源,释放后,锁的state会被重置为0,此函数的返回值是当前线程的重入锁的数量。为什么会释放线程所有的锁资源?因为Condition.await时表示线程进行等待状态,CPU让给其它线程执行。
//释放当前线程的锁资源,此时会释放线程的所有重入锁。
//==>目前此函数只会在Condition.await时调用(让线程等待,释放线程锁)
final int fullyRelease(Node node) {
boolean failed = true;
try {
//先得到重入锁的数据,
int savedState = getState();
//释放锁资源(请参考AQS.release),此时state的值变为0.
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
//如果释放锁资源失败,会标记此线程为CANCELLED状态.
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
isOnSyncQueue
此函数判断Condition线程是否在锁同步队列中,如果线程的waitStatus是CONDITION或者node.prev为null时,表示不在队列中返回false,其它情况返回true.
//判断线程是否在锁的同步队列中:
//==>如果线程节点是“CONDITION”或者线程没有前继节点返回false。
//==>如果线程还有后继节点,返回true,此时线程必然在队列中.
//==>迭代同步队列,从队尾节点向前查找,如果找到节点返回true,否则返回false表示不在队列中.
final boolean isOnSyncQueue(Node node) {
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
if (node.next != null) // If has successor, it must be on queue
return true;
/*
* node.prev can be non-null, but not yet on queue because
* the CAS to place it on queue can fail. So we have to
* traverse from tail to make sure it actually made it. It
* will always be near the tail in calls to this method, and
* unless the CAS failed (which is unlikely), it will be
* there, so we hardly ever traverse much.
*/
return findNodeFromTail(node);
}
//判断线程节点是否在同步队列中。
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
checkInterruptWhileWaiting
判断线程是否中断,如果线程未中断,直接返回0,否则尝试添加线程到锁同步队列,如果成功添加到同步队列,返回"THROW_IE:-1"(表示需要抛出异常),否则"REINTERRUPT:1"表示需要重入中断,在线程中断尝试添加同步队列时(原子操作waitStatus设置为0失败),表示有线程signal唤醒了当前线程,那么需要让出当前线程CPU执行时间,让signal的线程有机会把当前线程添加到锁的同步队列中。
//判断线程是否有中断信息,如果有,尝试把线程添加到锁同步队列中,
//==>如果在添加到同步队列前,线程被signal唤醒,返回"REINTERRUPT:1"
//==>如果成功添加线程到锁同步队列,返回"THROW_IE:-1",此时线程的waitStatus值被设置为0
//==>如果线程未中断,返回0.
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
//尝试把已经中断的线程添加到锁同步队列中,
//==>如果成功添加到锁同步队列,设置线程的waitStatus为0.
//==>否则进行CAS自旋,让出CPU执行时间,等待signal唤醒线程后把线程添加到锁同步队列.
final boolean transferAfterCancelledWait(Node node) {
if (compareAndSetWaitStatus(node, Node.CONDITION, 0)) {
enq(node);
return true;
}
/*
* If we lost out to a signal(), then we can't proceed
* until it finishes its enq(). Cancelling during an
* incomplete transfer is both rare and transient, so just
* spin.
*/
while (!isOnSyncQueue(node))
Thread.yield();
return false;
}
unlinkCancelledWaiters
移出Condition等待队列中被取消的线程,并重新更新队列的头尾节点。
//从Condition的等待队列中移出被取消的线程,
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null;
//从队列的头部节点开始向下查找,
//==>如果线程的waitStatue不是“CONDITION”状态,表示线程被取消,从队列中移出此节点.
//==>移出节点后,重新更新队列的头尾节点.
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;
}
}
signal
signal函数用于唤醒Condition等待队列中的队顶线程,此函数采用CAS自旋锁直到唤醒成功结束(或者等待队列为空),如果成功唤醒一个线程,会把线程添加到锁的同步队列中。
//Condition唤醒一个等待中的线程(与await对应),
public final void signal() {
//判断当前线程是否锁持有者线程,只有持有锁的线程才能唤醒其它线程.
//isHeldExclusively函数在ReentrantLock中实现(判断线程是否owner)
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
//唤醒Condition等待队列队顶线程。
Node first = firstWaiter;
//如果等待队列没有线程,啥都不做.
if (first != null)
doSignal(first);
}
//signal的具体唤醒函数,唤醒等待队列的队顶元素线程,并更新队列指针.
private void doSignal(Node first) {
//CAS自旋,尝试唤醒队顶元素线程
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
transferForSignal
此函数是Condition处理signal唤醒线程的具体实现,分为如下几种情况:
1,线程当前waitStatus状态不是”CONDITION”,说明线程被取消,直接返回false结束.
2,线程添加到锁同步队列,此时返回true.
2,1,判断线程的前继节点是否是取消状态,如果是直接唤醒线程。
2,2,尝试修改前继节点的waitStatus为”SIGNAL”,如果失败直接唤醒线程。
2,3,此时线程由锁同步队列中线程的前继节点来处理唤醒.
//Condition处理signal信号,即尝试唤醒等待中的线程,
//=>1,如果线程被取消(线程的waitStatus!=CONDITION),直接结束流程返回false.
//=>2,把线程添加到锁的同步队列中,并得到线程对应的前继节点,
//==>1,如果前继节点被取消或者尝试修改前继节点的wiatStatus为"SIGNAL"失败,
//======>直接唤醒node对应的线程(这种情况是暂时的说明前继节点有其它线程在处理waitStatus).
//==>2,添加线程到锁同步队列成功,此时对线程的前继节点设置有信号,表示前继节点需要唤醒线程.
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 ws = p.waitStatus;
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
LockSupport.unpark(node.thread);
return true;
}
本文深入解析了Java并发编程中ReentrantLock的Condition接口,对比了它与Object监视器方法的差异。文章通过ArrayBlockingQueue示例展示了如何使用Condition,并详细介绍了Condition的实现原理,包括ConditionObject的内部结构、await方法、signal方法及其相关操作,如线程等待、唤醒等机制。
735

被折叠的 条评论
为什么被折叠?



