在并发编程中,synchronized是和wait()、notify()、notifyAll()实现的等待通知机制。而比synchronized更强大的Lock同样实现了自己的最佳队友那就是Condition的await()、signal()、signalAll()。下面就以可重入锁ReentrantLock为例分析下Lock和Condition如果实现线程间的通信。
Condition与Lock一样也是接口,来看下接口的具体方法
//线程等待
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();
在可重入锁ReentrantLock中提供了 newCondition()方法实例化ConditionObject对象,这也就是提供了一个和Lock绑定的Condtion。而ConditionObject是队列同步器AQS(AbstractQueuedSynchronizer)的内部类,实现了Condition和Serializable序列化接口。主要来分析下await()、signal()、signalAll()三个方法。
两个重要的变量:
在Condtion类中实现了一个单链表实现的条件队列
//条件队列的首节点
private transient Node firstWaiter;
//条件队列的尾节点
private transient Node lastWaiter;
节点的等待状态
/**waitStatus value to indicate thread has cancelled
*因为中断或者超时,该线程已经取消
*/
static final int CANCELLED = 1;
/** waitStatus value to indicate successor's thread needs unparking
*现在正在被阻塞或者已经阻塞
*/
static final int SIGNAL = -1;
/** waitStatus value to indicate thread is waiting on condition
*表示该线程被处于条件队列中,就是因为调用了Condition.awaut而表示阻塞
*/
static final int CONDITION = -2;
/**
* waitStatus value to indicate the next acquireShared should
* unconditionally propagate
* 传播共享锁
*/
static final int PROPAGATE = -3;
线程进入等待 awati()
- 判断线程是否已经中断,中断的线程直接抛异常
- 将当前线程加入到阻塞队列中,将入阻塞队列之前调用(3)unlinkCancelledWaiters()清除掉阻塞队列中的状态为非阻塞的节点;新建当前线程的节点,节点状态为阻塞,如果当尾节点为空,则当前节点赋值首节点,否则设置为尾节点的后继节点,当前节点为新的尾节点返回,继续执行方法(4)fullyRelease。
- unlinkCancelledWaiters是重构队列的方法,将不是阻塞状态的节点剔除掉,里边有一个很重要的条件t.waitStatus != Node.CONDITION
- fullyRelease是放锁的方法与unlock调用的方法相同。接下来执行(5)
- isOnSyncQueue(Node node)主要判断线程是否在同步阻塞队列中,这一块也是笔记晦涩难以看懂的内容,涉及节点的状态转换,主要有三种情况: 5.1.如果节点状态是Node.CONDITION 或者node.prev == null表示节点未在条件队列中,返回为false 5.2.节点的后继节点不为null,表示节点在同步等待队列中,返回为true 5.3.还有一种情况是,前继节点不为空,但是也可能不在条件队列中,因为CAS可能会失败,
- 循环去判断节点的位置循环去判断节 点的位置,如果当前节点在是等待队列的尾节点,返回true,如果等待队列尾节点为空,返回false
- 检查线程是否中断,如果未中断则返回为0,如果未中断,则结束循环。
public final void await() throws InterruptedException {
//1.判断线程是否中断,如果中断直接抛出异常
if (Thread.interrupted())
throw new InterruptedException();
//2.将新节点加入到条件队列中
Node node = addConditionWaiter();(2)
//4.释放锁
int savedState = fullyRelease(node);
int interruptMode = 0;
//5.循环判断节点是否在同步队列中
while (!isOnSyncQueue(node)) {
//不在同步队列中,则阻塞线程
LockSupport.park(this);
//检查线程是否中断,如果未中断则返回为0
//5-1
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
break;
}
//7.唤醒线程如果需要继续执行,仍需要获得锁,中断位不是THROW_IE
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;//赋值状态为中断
//节点的后继节点不为空,也就不是尾节点
if (node.nextWaiter != null) // clean up if cancelled
unlinkCancelledWaiters();//8.清空阻塞队列
//节点状态不是0
if (interruptMode != 0)
//9.根据此时线程的状态抛出异常或是返回中断位
reportInterruptAfterWait(interruptMode);
}
//2.将新节点加入到条件队列尾部
private Node addConditionWaiter() {
//获取尾节点
Node t = lastWaiter;
// If lastWaiter is cancelled, clean out.
//如果节点不为空,节点的状态为Node.CONDITION(表示线程正在条件队列中)
if (t != null && t.waitStatus != Node.CONDITION) {
//2-1去除非条件队列的节点,调整队列结构
unlinkCancelledWaiters();
//重新返回尾节点
t = lastWaiter;
}
//创建关联当前线程的新节点,设置为CONDITION
Node node = new Node(Thread.currentThread(), Node.CONDITION);
//如果尾节点为空,将新节点设置为首节点
if (t == null)
firstWaiter = node;
else
//否则直接追加首节点后
t.nextWaiter = node;
//将新节点设置到尾节点直接返回
lastWaiter = node;
return node;
}
//2-1删除不是不符合条件的队列
private void unlinkCancelledWaiters() {
//获取首节点
Node t = firstWaiter;
Node trail = null;//新建一个临时节点
//如果首节点不为空
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//如果现在首节点是CONDITION,直接将节点赋值给临时节点,后继节点变为首节点继续循环
trail = t;
t = next;
}
}
//3.await实际就是wait,而wait会释放锁,所以这一步会释放资源,修改state的值,
//可以参考unlock方法分析,如果出现问题会修改节点状态为Node.CANCELLED中断或者超时
final int fullyRelease(Node node) {
boolean failed = true;
try {
//获取锁的状态
int savedState = getState();
if (release(savedState)) {
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
//5.判断线程是否在同步队列中
final boolean isOnSyncQueue(Node node) {
//如果节点状态是Node.CONDITION 或者node.prev == null表示节点未在同步队列中
if (node.waitStatus == Node.CONDITION || node.prev == null)
return false;
//节点的后继节点不为null,表示节点在同步队列中
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.
*/
//还有一种情况是,前继节点不为空,next为空的情况,但是也可能在同步队列中,因为加队同步队列的CAS可能会失败,那加入节点的位置,可能在队列尾结点也可能在队列中,为了保险需从尾部开始查找节点
//6.循环去判断节点的位置
return findNodeFromTail(node);
}
//5-1
private int checkInterruptWhileWaiting(Node node) {
return Thread.interrupted() ?
(transferAfterCancelledWait(node) ? THROW_IE : REINTERRUPT) :
0;
}
//5-2
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;
}
//6.循环去判断节点的位置,如果当前节点在是同步队列的尾节点,返回true,如果同步队列尾节点为空
//返回false
private boolean findNodeFromTail(Node node) {
Node t = tail;
for (;;) {
if (t == node)
return true;
if (t == null)
return false;
t = t.prev;
}
}
//7.
final boolean acquireQueued(final Node node, int arg) {
boolean failed = true;
try {
boolean interrupted = false;
for (;;) {
final Node p = node.predecessor();
if (p == head && tryAcquire(arg)) {
setHead(node);
p.next = null; // help GC
failed = false;
return interrupted;
}
if (shouldParkAfterFailedAcquire(p, node) &&
parkAndCheckInterrupt())
interrupted = true;
}
} finally {
if (failed)
cancelAcquire(node);
}
}
//8.
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null;
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;
}
}
//9
private void reportInterruptAfterWait(int interruptMode)
throws InterruptedException {
if (interruptMode == THROW_IE)
throw new InterruptedException();
else if (interruptMode == REINTERRUPT)
selfInterrupt();
}
唤醒一个等待线程signal()
(1)只有获得锁的线程才能执行signalAll,否则会抛错
(2)换线线程的操作doSignal(Node first),循环剔除不在阻塞状态的线程,循环将要被通知的线程从阻塞队列中删除
(3)transferForSignal(Node node)将被通知的节点加入到等待队列中,唤醒被通知节点的线程。
signal和signalAll的区别主要signal只会将第一个等待通知的节点从阻塞队列中删除,而signalAll是将循环将要通知的节点从阻塞队列中移除,而通知线程唤醒的方法是一致的。
public final void signal() {
//判断当前线程是否是占用线程(是否获得锁),如果不是则抛错
if (!isHeldExclusively())//(1)
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignal(first);(2)唤醒线程真正的方法
}
//(1)判断当前线程是否获取锁
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
//(2)唤醒线程
private void doSignal(Node first) {
do {
//将要被通知的节点的后继节点设置为等待队列首节点,如果当前节点的下一个节点为空
if ( (firstWaiter = first.nextWaiter) == null)
//设置等待队列的尾结点为空
lastWaiter = null;
//将要被通知的节点的后继节点置为空,将被通知的节点脱离等待队列
first.nextWaiter = null;
//循环条件:剔除不是阻塞状态的节点,同时当前节点不为空
} while (!transferForSignal(first) &&//(3)
(first = firstWaiter) != null);
}
//(3)修改节点状态,唤醒线程
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
//通过原子CAS操作修改节点状态,如果修改失败(表示线程已经不是阻塞状态,不在阻塞队列中)返回false,返回循环(3)继续判断循环条件
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;//获取节点的等待状态
//节点状态>0,表示节点已经被取消或者已经不能把节点状态修改为阻塞
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
//唤醒线程
LockSupport.unpark(node.thread);
return true;
}
通知所有线程signalAll()
(1)只有获得锁的线程才能执行signalAll,否则会抛错
(2)换线线程的操作doSignal(Node first),循环剔除不在阻塞状态的线程,循环将要被通知的线程从阻塞队列中删除
(3)transferForSignal(Node node)将被通知的节点加入到等待队列中,唤醒被通知节点的线程。
signal和signalAll的区别主要signal只会将第一个等待通知的节点从阻塞队列中删除,而signalAll是将循环jian
public final void signalAll() {
//判断当前线程是否是占用线程(是否获得锁),如果不是则抛错
if (!isHeldExclusively())//(1)
throw new IllegalMonitorStateException();
Node first = firstWaiter;
if (first != null)
doSignalAll(first);//(2)唤醒线程的主要方法
}
//(1)判断当前线程是否获取锁
protected final boolean isHeldExclusively() {
// While we must in general read state before owner,
// we don't need to do so to check if current thread is owner
return getExclusiveOwnerThread() == Thread.currentThread();
}
//(2)唤醒线程
private void doSignalAll(Node first) {
//将首节点和尾节点都置为空
lastWaiter = firstWaiter = null;
do {
//将被通知节点从阻塞队列中移除
Node next = first.nextWaiter;
first.nextWaiter = null;
transferForSignal(first);//(3)加入等待队列,唤醒线程
first = next;
//循环条件:被通知节点不为空
} while (first != null);
}
//(3)
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;
}