在併發編程中,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;
}