本文是在學習中的總結,歡迎轉載但請註明出處:http://blog.csdn.net/pistolove/article/details/105031259
1、前言
Condition實現關鍵:等待隊列。
- 等待隊列是一個FIFO的隊列,隊列中每個節點包含一個線程引用,該線程就是在Conditon對象上等待的線程。一個Condition對象包含一個等待隊列,Condition擁有首節點(firstWaiter)和尾結點(lastWaiter)。當線程調用Conditon.await()方法,將會以當前線程構造節點,並將節點從尾部加入到等待隊列。
Object和Condition對比:
- Object是java底層級別的;Condition是語言級別的,具有更高的可控制性和擴展性。
- Object的wait和notify/notify是與對象監視器配合完成線程間的等待/通知機制;Condition與Lock配合完成等待通知機制。
- Condition能夠支持不響應中斷;Object方式不支持;
- Condition能夠支持多個等待隊列(new 多個Condition對象);Object方式只能支持一個;
- Condition能夠支持超時時間的設置;Object方式不支持。
2、Condition代碼示例
- 基於Condition生產者消費者代碼實現如下所示,先了解一下Condition是如何使用的。
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;
/**
* 基於Condition生產者消費者代碼實現
*
* @param <T>
*/
public class ConditionDemo<T> {
private ReentrantLock reentrantLock = new ReentrantLock();
private Condition consumerCondition = reentrantLock.newCondition();
private Condition producerCondition = reentrantLock.newCondition();
private int count, addIndex, removeIndex;
private Object[] goods;
public ConditionDemo(int size) {
goods = new Object[size];
}
/**
* 生產商品
*
* @param t
*/
public void produce(T t) {
reentrantLock.lock();
try {
while (count == goods.length)
producerCondition.await();
goods[addIndex] = t;
if (++addIndex == goods.length) {
addIndex = 0;
}
++count;
System.out.println("produce: " + t);
consumerCondition.signal();
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
}
/**
* 消費產品
*
* @return
*/
public T consume() {
reentrantLock.lock();
try {
while (count == 0)
consumerCondition.await();
Object x = goods[removeIndex];
if (++removeIndex == goods.length) {
removeIndex = 0;
}
--count;
producerCondition.signal();
System.out.println("consume: " + x);
return (T) x;
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
reentrantLock.unlock();
}
return null;
}
public static void main(String[] args) {
ConditionDemo<String> conditionDemo = new ConditionDemo(10);
new Thread() {
@Override
public void run() {
int i = 1;
while (i != 100) {
conditionDemo.produce("" + i++);
}
}
}.start();
new Thread() {
@Override
public void run() {
while (true) {
conditionDemo.consume();
}
}
}.start();
}
}
3、類關係圖
- new Condition()其實是創建了一個ConditionObject,ConditionObject實現了Condition接口和Serializable接口。
4、源碼解讀
-
await()方法源碼解讀
- 使當前線程進入等待隊列,並釋放鎖,同時線程狀態變爲等待狀態;相當於同步隊列的首節點移到了Condition的等待隊列中。
public final void await() throws InterruptedException {
// 線程中斷則拋出異常
if (Thread.interrupted())
throw new InterruptedException();
// 當前線程加入Conditon等待隊列
Node node = addConditionWaiter();
// 釋放同步狀態,即釋放同步鎖;喚醒當前線程的節點(頭節點)的後繼節點
int savedState = fullyRelease(node);
int interruptMode = 0;
// 如果節點不在同步隊列中
while (!isOnSyncQueue(node)) {
// 阻塞當前線程
LockSupport.park(this);
// THROW_IE if interrupted before signalled
// REINTERRUPT if after signalled
// 如果中斷被喚醒,循環停止
// 判斷此次線程的喚醒是否因爲線程被中斷,
// 若是被中斷, 則會在checkInterruptWhileWaiting的transferAfterCancelledWait進行節點的轉移;
// 返回值interruptMode != 0
if ((interruptMode = checkInterruptWhileWaiting(node)) != 0)
// 通過線程中斷的方式進行喚醒, 並且已經進行node的轉移, 轉移到 Sync Queue裏面
break;
}
// 調用 acquireQueued在Sync Queue裏面進行獨佔鎖的獲取, 返回值表明在獲取的過程中有沒有被中斷過
if (acquireQueued(node, savedState) && interruptMode != THROW_IE)
interruptMode = REINTERRUPT;
// 判斷線程的喚醒是中斷還是signal
// 如通過中斷喚醒的話, 此刻代表線程的Node在Condition Queue與Sync Queue裏面都會存在
if (node.nextWaiter != null) // clean up if cancelled
// cancelled節點的清除
unlinkCancelledWaiters();
// 通過中斷的方式喚醒線程
if (interruptMode != 0)
// 根據 interruptMode 的類型決定是拋出異常, 還是自己中斷一下
reportInterruptAfterWait(interruptMode);
}
// 加入到等待隊列
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;
}
// 移除狀態爲已取消的節點
private void unlinkCancelledWaiters() {
Node t = firstWaiter;
Node trail = null;
while (t != null) {
Node next = t.nextWaiter;
// 如果狀態不是條件等待
if (t.waitStatus != Node.CONDITION) {
//刪除next引用
t.nextWaiter = null;
if (trail == null)
firstWaiter = next;
else
trail.nextWaiter = next;
if (next == null)
lastWaiter = trail;
}
else
trail = t;
t = next;
}
}
// 釋放鎖
final int fullyRelease(Node node) {
boolean failed = true;
try {
// 獲取當前同步狀態
int savedState = getState();
//釋放鎖
if (release(savedState)) {
// 釋放鎖成功,返回同步狀態
failed = false;
return savedState;
} else {
throw new IllegalMonitorStateException();
}
} finally {
// 釋放鎖失敗,狀態置爲CANCELLED
if (failed)
node.waitStatus = Node.CANCELLED;
}
}
// 釋放完成鎖之後,需要喚醒後繼節點
public final boolean release(int arg) {
if (tryRelease(arg)) {
Node h = head;
if (h != null && h.waitStatus != 0)
unparkSuccessor(h);
return true;
}
return false;
}
// 獲取鎖
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);
}
}
// 節點是否在阻塞隊列中
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;
}
}
- signal()方法源碼解讀
- 將等待隊列中等待時間最長的節點移動到同步隊列中,使得該節點能夠有機會獲得lock。等待隊列是先進先出(FIFO),等待隊列頭節點必然是等待時間最長的節點,每次調用condition的signal方法是將頭節點移動到同步隊列中。
public final void signal() {
// 當前線程必須獲取鎖才行
if (!isHeldExclusively())
throw new IllegalMonitorStateException();
// 喚醒等待隊列中的第一個節點
Node first = firstWaiter;
if (first != null)
doSignal(first);
}
// 節點從等待隊列移到同步隊列
private void doSignal(Node first) {
do {
// 如果等待隊列值有一個節點
if ( (firstWaiter = first.nextWaiter) == null)
// lastWaiter置爲空
lastWaiter = null;
// first節點從等待隊列中出來
// 判斷Node從Condition queue轉移到Sync Queue裏是通過signal還是timeout/interrupt
first.nextWaiter = null;
} while (!transferForSignal(first) &&
(first = firstWaiter) != null);
// 調用transferForSignal將first轉移到Sync Queue裏
// 不成功, 則將firstWaiter重新賦值給first
}
// 加入到阻塞隊列
final boolean transferForSignal(Node node) {
/*
* If cannot change waitStatus, the node has been cancelled.
*/
// 如果無法更新同步狀態,節點狀態爲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;
// 如果前驅節點狀態爲超時或者中斷,或者CAS設置喚醒狀態失敗
if (ws > 0 || !compareAndSetWaitStatus(p, ws, Node.SIGNAL))
// 喚醒當前線程
LockSupport.unpark(node.thread);
return true;
}