Condition的概念
Condition是一个接口,默认实现是Lock里的ConditionObject。创建方式一般是lock.newCondition();主要是用来实现等待/通知功能的。
Condition的源码简单分析
Condition接口定义
public interface Condition {
void await() throws InterruptedException;
void awaitUninterruptibly();
long awaitNanos(long nanosTimeout) throws InterruptedException;
boolean await(long time, TimeUnit unit) throws InterruptedException;
boolean awaitUntil(Date deadline) throws InterruptedException;
void signal();
void signalAll();
}
主要就是等待/通知方法。
在看Condition的实现类ConditionObject。ConditionObject实现了Condition接口,和Serializable。
public class ConditionObject implements Condition, java.io.Serializable {
private static final long serialVersionUID = 1173984872572414699L;
/** First node of condition queue. */
private transient Node firstWaiter;
/** Last node of condition queue. */
private transient Node lastWaiter;
从属性可以看到,和其他的同步队列一样,使用的都是AQS的节点Node类。并维护了首节点和尾节点。
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) // clean up if cancelled
unlinkCancelledWaiters();
if (interruptMode != 0)
reportInterruptAfterWait(interruptMode);
}
从上面代码可以总结出await的方法流程
- 当前线程是否被中断,中断的话就抛出异常。
- 把当前线程创建为node,放入等待队列。
- 把当前线程占有的同步锁释放。
- 循环判断当前线程是否已经放入队列,已经加入了,就直接退出,不然就阻塞,循环判断。
- 调用 acquireQueued() 方法加入同步状态竞争
下面看看是如何把当前线程创建为Node的
addConditionWaiter
private Node addConditionWaiter() {
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
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;
}
- 清除所有状态不为CONDITION的节点
- 创建node实例,把新创建的node放在队列的尾部
通知
signal()
public final void signal() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
- 判断当前线程是否拥有锁
- 对首节点做doSignal处理。。
因为condition的增加操作是放在队尾的,唤醒时是先对首节点唤醒,所以是FIFO。
doSignal
private void doSignal(Node first) {
do {
if ( (firstWaiter = first.nextWaiter) == null)
lastWaiter = null;
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
}
方法很简单,主要看transferForSignal,transferForSignal是把节点移动到同步队列里
transferForSignal
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;
}
- 把节点的状态设为0,失败就直接返回false
- 调用enq方法,将节点设置到同步队列里
- 如果节点的状态为CANCELLED或者修改waitStatus失败,就直接唤醒
signalAll()
public final void signalAll() {
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);
}
private void doSignalAll(Node first) {
lastWaiter = firstWaiter = null;
do {
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);
first = next;
} while (first != null);
}
大家可以看到,singalAll方法很简单,主要就是从头节点开始,把所有节点都移动到同步队列里,并把每个线程都唤醒。
下面来总结一下COndition的工作流程吧
- 获取到锁的线程,因为什么东西还未满足继续执行的条件,就调用Condition.await()方法,await会将当前线程加入到等待队列里,然后一直在await的while循环里等待当前节点是否已经被放在同步队列里,在同步队列里的话就尝试去获取锁,否则就一直阻塞。
- 当其他的线程调用singal()方法时,首先判断是否已经获取了锁没,然后调用doSignal方法把头节点放入同步队列里,并使用 LockSupport.unpark唤醒其中的线程。
- 被唤醒的线程会从await()方法里的while循环里推出来,然后调用acquireQueued去竞争锁,竞争成功就会推出await方法,线程继续网下执行。