概述
Phaser是JDK 7新增的一個同步輔助類,在功能上跟CyclicBarrier和CountDownLatch差不多,但支持更豐富的用法。與JUC中多數同步類不同,它并不是通過AQS來實現的,而是用了另外一種同步機制。本文我們將從多個方面分析Phaser這個同步類。
Phaser運行示意圖:
在開始Phaser的深入分析之前,我們先大概看一下對于Phaser
的一些概念介紹(由于涉及Phaser
的部分方法,可結合源碼分析學習本節內容):
Registration(注冊):
跟其他barrier不同,在phaser上注冊的parties會隨著時間的變化而變化。任務可以隨時注冊(使用方法register
,bulkRegister
注冊,或者由構造器確定初始parties),并且在任何抵達點可以隨意地撤銷注冊(方法arriveAndDeregister
)。就像大多數基本的同步結構一樣,注冊和撤銷只影響內部count;不會創建更深的內部記錄,所以任務不能查詢他們是否已經注冊。(不過,可以通過繼承來實現類似的記錄)-
Synchronization(同步機制):
和CyclicBarrier
一樣,Phaser
也可以重復await。方法arriveAndAwaitAdvance
的效果類似CyclicBarrier.await
。phaser的每一代都有一個相關的phase number
,初始值為0,當所有注冊的任務都到達phaser時phase+1,到達最大值(Integer.MAX_VALUE
)之后清零。使用phase number
可以獨立控制 到達phaser 和 等待其他線程 的動作,通過下面兩種類型的方法:-
Arrival(到達機制)
arrive
和arriveAndDeregister
方法記錄到達狀態。這些方法不會阻塞,但是會返回一個相關的arrival phase number
;也就是說,phase number
用來確定到達狀態。當所有任務都到達給定phase時,可以執行一個可選的函數,這個函數通過重寫onAdvance
方法實現,通常可以用來控制終止狀態。重寫此方法類似于為CyclicBarrier
提供一個barrierAction
,但比它更靈活。 -
Waiting(等待機制)
awaitAdvance
方法需要一個表示arrival phase number
的參數,并且在phaser前進到與給定phase不同的phase時返回。和CyclicBarrier
不同,即使等待線程已經被中斷,awaitAdvance
方法也會一直等待。中斷狀態和超時時間同樣可用,但是當任務等待中斷或超時后未改變phaser的狀態時會遭遇異常。如果有必要,在方法forceTermination
之后可以執行這些異常的相關的handler進行恢復操作,Phaser
也可能被ForkJoinPool
中的任務使用,這樣在其他任務阻塞等待一個phase時可以保證足夠的并行度來執行任務。
-
Arrival(到達機制)
Termination(終止機制):
可以用isTerminated
方法檢查Phaser
的終止狀態。在終止時,所有同步方法立刻返回一個負值。在終止時嘗試注冊也沒有效果。當調用onAdvance
返回true
時Termination被觸發。當deregistration
操作使已注冊的parties變為0時,onAdvance
的默認實現就會返回true
。也可以重寫onAdvance
方法來定義終止動作。forceTermination
方法也可以釋放等待線程并且允許它們終止。Tiering(分層結構):
Phaser
支持分層結構(樹狀構造)來減少競爭。注冊了大量parties的Phaser
可能會因為同步競爭消耗很高的成本, 因此可以設置一些子Phaser
來共享一個通用的parent。這樣的話即使每個操作消耗了更多的開銷,但是會提高整體吞吐量。
在一個分層結構的phaser
里,子節點phaser
的注冊和取消注冊都通過父節點管理。子節點phaser
通過構造或方法register、bulkRegister
進行首次注冊時,在其父節點上注冊。子節點phaser通過調用arriveAndDeregister
進行最后一次取消注冊時,也在其父節點上取消注冊。Monitoring(狀態監控):
由于同步方法可能只被已注冊的parties調用,所以phaser的當前狀態也可能被任何調用者監控。在任何時候,可以通過getRegisteredParties
獲取parties數,其中getArrivedParties
方法返回已經到達當前phase的parties數。當剩余的parties(通過方法getUnarrivedParties獲取)到達時,phase進入下一代。這些方法返回的值可能只表示短暫的狀態(同步類的通病),所以一般來說在同步結構里并沒有啥卵用。
1. 核心參數和函數列表
private volatile long state;
/**
* The parent of this phaser, or null if none
*/
private final Phaser parent;
/**
* The root of phaser tree. Equals this if not in a tree.
*/
private final Phaser root;
//等待線程的棧頂元素,根據phase取模定義為一個奇數header和一個偶數header
private final AtomicReference<QNode> evenQ;
private final AtomicReference<QNode> oddQ;
state狀態說明:
Phaser使用一個long型state
值來標識內部狀態:
- 低0-15位表示未到達parties數;
- 中16-31位表示等待的parties數;
- 中32-62位表示phase當前代;
- 高63位表示當前phaser的終止狀態。
注意:子Phaser的phase在沒有被真正使用之前,允許滯后于它的root節點。這里在后面源碼分析的reconcileState
方法里會講解。
Qnode
是Phaser定義的內部等待隊列,用于在阻塞時記錄等待線程及相關信息。實現了ForkJoinPool的一個內部接口ManagedBlocker
,上面已經說過,Phaser
也可能被ForkJoinPool
中的任務使用,這樣在其他任務阻塞等待一個phase時可以保證足夠的并行度來執行任務(通過內部實現方法isReleasable
和block
)。
函數列表:
//構造方法
public Phaser() {
this(null, 0);
}
public Phaser(int parties) {
this(null, parties);
}
public Phaser(Phaser parent) {
this(parent, 0);
}
public Phaser(Phaser parent, int parties)
//注冊一個新的party
public int register()
//批量注冊
public int bulkRegister(int parties)
//使當前線程到達phaser,不等待其他任務到達。返回arrival phase number
public int arrive()
//使當前線程到達phaser并撤銷注冊,返回arrival phase number
public int arriveAndDeregister()
/*
* 使當前線程到達phaser并等待其他任務到達,等價于awaitAdvance(arrive())。
* 如果需要等待中斷或超時,可以使用awaitAdvance方法完成一個類似的構造。
* 如果需要在到達后取消注冊,可以使用awaitAdvance(arriveAndDeregister())。
*/
public int arriveAndAwaitAdvance()
//等待給定phase數,返回下一個 arrival phase number
public int awaitAdvance(int phase)
//阻塞等待,直到phase前進到下一代,返回下一代的phase number
public int awaitAdvance(int phase)
//響應中斷版awaitAdvance
public int awaitAdvanceInterruptibly(int phase) throws InterruptedException
public int awaitAdvanceInterruptibly(int phase, long timeout, TimeUnit unit)
throws InterruptedException, TimeoutException
//使當前phaser進入終止狀態,已注冊的parties不受影響,如果是分層結構,則終止所有phaser
public void forceTermination()
2. 使用示例
public class PhaserTest1 {
private static Phaser ps;
private static int SIZE = 10;
public static void main(String[] args) throws InterruptedException {
ps = new Phaser(SIZE);
List<Runnable> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
list.add(new MyTask());
}
//runTasks(list);
startTasks(list, 3);
}
static class MyTask implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + " wait for phaser");
ps.arriveAndAwaitAdvance();
//ps.awaitAdvance(ps.arrive());
System.out.println(Thread.currentThread().getName() + " continued");
}
}
//替代CountDownLatch
static void runTasks(List<Runnable> tasks) {
final Phaser phaser = new Phaser(1); // "1" to register self
// create and start threads
for (final Runnable task : tasks) {
phaser.register();
new Thread() {
public void run() {
System.out.println("wait phaser");
phaser.arriveAndAwaitAdvance(); // await all creation
//phaser.awaitAdvance(2);
System.out.println("phaser is done");
task.run();
}
}.start();
}
// allow threads to start and deregister self
phaser.arriveAndDeregister();
}
//使用多階(phase)
static void startTasks(List<Runnable> tasks, final int iterations) {
final Phaser phaser = new Phaser() {
protected boolean onAdvance(int phase, int registeredParties) {
return phase >= iterations || registeredParties == 0;
}
};
phaser.register();
for (final Runnable task : tasks) {
phaser.register();
new Thread() {
public void run() {
do {
task.run();
phaser.arriveAndAwaitAdvance();
} while (!phaser.isTerminated());
}
}.start();
}
phaser.arriveAndDeregister(); // deregister self, don't wait
}
}
3. 源碼分析
3.1 register()
//注冊一個新的party
public int register() {
return doRegister(1);
}
private int doRegister(int registrations) {
// adjustment to state
long adjust = ((long)registrations << PARTIES_SHIFT) | registrations;
final Phaser parent = this.parent;
int phase;
for (;;) {
long s = (parent == null) ? state : reconcileState();
int counts = (int)s;
int parties = counts >>> PARTIES_SHIFT;//獲取已注冊parties數
int unarrived = counts & UNARRIVED_MASK;//未到達數
if (registrations > MAX_PARTIES - parties)
throw new IllegalStateException(badRegister(s));
phase = (int)(s >>> PHASE_SHIFT);//獲取當前代
if (phase < 0)
break;
if (counts != EMPTY) { // not 1st registration
if (parent == null || reconcileState() == s) {
if (unarrived == 0) // wait out advance
root.internalAwaitAdvance(phase, null);//等待其他任務到達
else if (UNSAFE.compareAndSwapLong(this, stateOffset,
s, s + adjust))//更新注冊的parties數
break;
}
}
else if (parent == null) { // 1st root registration
long next = ((long)phase << PHASE_SHIFT) | adjust;
if (UNSAFE.compareAndSwapLong(this, stateOffset, s, next))//更新phase
break;
}
else {
//分層結構,子phaser首次注冊用父節點管理
synchronized (this) { // 1st sub registration
if (state == s) { // recheck under lock
phase = parent.doRegister(1);//分層結構,使用父節點注冊
if (phase < 0)
break;
// finish registration whenever parent registration
// succeeded, even when racing with termination,
// since these are part of the same "transaction".
//由于在同一個事務里,即使phaser已終止,也會完成注冊
while (!UNSAFE.compareAndSwapLong
(this, stateOffset, s,
((long)phase << PHASE_SHIFT) | adjust)) {//更新phase
s = state;
phase = (int)(root.state >>> PHASE_SHIFT);
// assert (int)s == EMPTY;
}
break;
}
}
}
}
return phase;
}
說明: register
方法為phaser添加一個新的party,如果onAdvance
正在運行,那么這個方法會等待它運行結束再返回結果。如果當前phaser有父節點,并且當前phaser上沒有已注冊的party,那么就會交給父節點注冊。
register
和bulkRegister
都由doRegister實現,大概流程如下:
- 如果當前操作不是首次注冊,那么直接在當前phaser上更新注冊parties數
- 如果是首次注冊,并且當前phaser沒有父節點,說明是root節點注冊,直接更新phase
- 如果當前操作是首次注冊,并且當前phaser由父節點,則注冊操作交由父節點,并更新當前phaser的phase
上面說過,子Phaser的phase在沒有被真正使用之前,允許滯后于它的root節點。非首次注冊時,如果Phaser有父節點,則調用reconcileState()
方法解決root節點的phase延遲傳遞問題, 源碼如下:
private long reconcileState() {
final Phaser root = this.root;
long s = state;
if (root != this) {
int phase, p;
// CAS to root phase with current parties, tripping unarrived
while ((phase = (int)(root.state >>> PHASE_SHIFT)) !=
(int)(s >>> PHASE_SHIFT) &&
!UNSAFE.compareAndSwapLong
(this, stateOffset, s,
s = (((long)phase << PHASE_SHIFT) |
((phase < 0) ? (s & COUNTS_MASK) :
(((p = (int)s >>> PARTIES_SHIFT) == 0) ? EMPTY :
((s & PARTIES_MASK) | p))))))
s = state;
}
return s;
}
當root節點的phase已經advance到下一代,但是子節點phaser還沒有,這種情況下它們必須通過更新未到達parties數 完成它們自己的advance操作(如果parties為0,重置為EMPTY
狀態)。
回到register方法的第一步,如果當前未到達數為0,說明上一代phase正在進行到達操作,此時調用internalAwaitAdvance()
方法等待其他任務完成到達操作,源碼如下:
//阻塞等待phase到下一代
private int internalAwaitAdvance(int phase, QNode node) {
// assert root == this;
releaseWaiters(phase-1); // ensure old queue clean
boolean queued = false; // true when node is enqueued
int lastUnarrived = 0; // to increase spins upon change
int spins = SPINS_PER_ARRIVAL;
long s;
int p;
while ((p = (int)((s = state) >>> PHASE_SHIFT)) == phase) {
if (node == null) { // spinning in noninterruptible mode
int unarrived = (int)s & UNARRIVED_MASK;//未到達數
if (unarrived != lastUnarrived &&
(lastUnarrived = unarrived) < NCPU)
spins += SPINS_PER_ARRIVAL;
boolean interrupted = Thread.interrupted();
if (interrupted || --spins < 0) { // need node to record intr
//使用node記錄中斷狀態
node = new QNode(this, phase, false, false, 0L);
node.wasInterrupted = interrupted;
}
}
else if (node.isReleasable()) // done or aborted
break;
else if (!queued) { // push onto queue
AtomicReference<QNode> head = (phase & 1) == 0 ? evenQ : oddQ;
QNode q = node.next = head.get();
if ((q == null || q.phase == phase) &&
(int)(state >>> PHASE_SHIFT) == phase) // avoid stale enq
queued = head.compareAndSet(q, node);
}
else {
try {
ForkJoinPool.managedBlock(node);//阻塞給定node
} catch (InterruptedException ie) {
node.wasInterrupted = true;
}
}
}
if (node != null) {
if (node.thread != null)
node.thread = null; // avoid need for unpark()
if (node.wasInterrupted && !node.interruptible)
Thread.currentThread().interrupt();
if (p == phase && (p = (int)(state >>> PHASE_SHIFT)) == phase)
return abortWait(phase); // possibly clean up on abort
}
releaseWaiters(phase);
return p;
}
簡單介紹下第二個參數node,如果不為空,則說明等待線程需要追蹤中斷狀態或超時狀態。以doRegister
中的調用為例,不考慮線程爭用,internalAwaitAdvance
大概流程如下:
- 首先調用
releaseWaiters
喚醒上一代所有等待線程,確保舊隊列中沒有遺留的等待線程。 - 循環
SPINS_PER_ARRIVAL
指定的次數或者當前線程被中斷,創建node記錄等待線程及相關信息。 - 繼續循環調用ForkJoinPool.managedBlock運行被阻塞的任務
- 繼續循環,阻塞任務運行成功被釋放,跳出循環
- 最后喚醒當前phase的線程
3.2 arrive()
//使當前線程到達phaser,不等待其他任務到達。返回arrival phase number
public int arrive() {
return doArrive(ONE_ARRIVAL);
}
private int doArrive(int adjust) {
final Phaser root = this.root;
for (;;) {
long s = (root == this) ? state : reconcileState();
int phase = (int)(s >>> PHASE_SHIFT);
if (phase < 0)
return phase;
int counts = (int)s;
//獲取未到達數
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);
if (unarrived <= 0)
throw new IllegalStateException(badArrive(s));
if (UNSAFE.compareAndSwapLong(this, stateOffset, s, s-=adjust)) {//更新state
if (unarrived == 1) {//當前為最后一個未到達的任務
long n = s & PARTIES_MASK; // base of next state
int nextUnarrived = (int)n >>> PARTIES_SHIFT;
if (root == this) {
if (onAdvance(phase, nextUnarrived))//檢查是否需要終止phaser
n |= TERMINATION_BIT;
else if (nextUnarrived == 0)
n |= EMPTY;
else
n |= nextUnarrived;
int nextPhase = (phase + 1) & MAX_PHASE;
n |= (long)nextPhase << PHASE_SHIFT;
UNSAFE.compareAndSwapLong(this, stateOffset, s, n);
releaseWaiters(phase);//釋放等待phase的線程
}
//分層結構,使用父節點管理arrive
else if (nextUnarrived == 0) { //propagate deregistration
phase = parent.doArrive(ONE_DEREGISTER);
UNSAFE.compareAndSwapLong(this, stateOffset,
s, s | EMPTY);
}
else
phase = parent.doArrive(ONE_ARRIVAL);
}
return phase;
}
}
}
說明: arrive
方法手動調整到達數,使當前線程到達phaser。arrive
和arriveAndDeregister
都調用了doArrive
實現,大概流程如下:
- 首先更新
state(state - adjust)
; - 如果當前不是最后一個未到達的任務,直接返回phase
- 如果當前是最后一個未到達的任務:
a) 如果當前是root節點,判斷是否需要終止phaser,CAS
更新phase,最后釋放等待的線程;
b) 如果是分層結構,并且已經沒有下一代未到達的parties,則交由父節點處理doArrive
邏輯,然后更新state為EMPTY
。
3.3 arriveAndAwaitAdvance()
public int arriveAndAwaitAdvance() {
// Specialization of doArrive+awaitAdvance eliminating some reads/paths
final Phaser root = this.root;
for (;;) {
long s = (root == this) ? state : reconcileState();
int phase = (int)(s >>> PHASE_SHIFT);
if (phase < 0)
return phase;
int counts = (int)s;
int unarrived = (counts == EMPTY) ? 0 : (counts & UNARRIVED_MASK);//獲取未到達數
if (unarrived <= 0)
throw new IllegalStateException(badArrive(s));
if (UNSAFE.compareAndSwapLong(this, stateOffset, s,
s -= ONE_ARRIVAL)) {//更新state
if (unarrived > 1)
return root.internalAwaitAdvance(phase, null);//阻塞等待其他任務
if (root != this)
return parent.arriveAndAwaitAdvance();//子Phaser交給父節點處理
long n = s & PARTIES_MASK; // base of next state
int nextUnarrived = (int)n >>> PARTIES_SHIFT;
if (onAdvance(phase, nextUnarrived))//全部到達,檢查是否可銷毀
n |= TERMINATION_BIT;
else if (nextUnarrived == 0)
n |= EMPTY;
else
n |= nextUnarrived;
int nextPhase = (phase + 1) & MAX_PHASE;//計算下一代phase
n |= (long)nextPhase << PHASE_SHIFT;
if (!UNSAFE.compareAndSwapLong(this, stateOffset, s, n))//更新state
return (int)(state >>> PHASE_SHIFT); // terminated
releaseWaiters(phase);//釋放等待phase的線程
return nextPhase;
}
}
}
說明:使當前線程到達phaser并等待其他任務到達,等價于awaitAdvance(arrive())
。如果需要等待中斷或超時,可以使用awaitAdvance
方法完成一個類似的構造。如果需要在到達后取消注冊,可以使用awaitAdvance(arriveAndDeregister())
。效果類似于CyclicBarrier.await
。大概流程如下:
- 更新state(state - 1);
- 如果未到達數大于1,調用
internalAwaitAdvance
阻塞等待其他任務到達,返回當前phase - 如果為分層結構,則交由父節點處理
arriveAndAwaitAdvance
邏輯 - 如果未到達數<=1,判斷phaser終止狀態,CAS更新phase到下一代,最后釋放等待當前phase的線程,并返回下一代phase。
3.4 awaitAdvance(int phase)
public int awaitAdvance(int phase) {
final Phaser root = this.root;
long s = (root == this) ? state : reconcileState();
int p = (int)(s >>> PHASE_SHIFT);
if (phase < 0)
return phase;
if (p == phase)
return root.internalAwaitAdvance(phase, null);
return p;
}
//響應中斷版awaitAdvance
public int awaitAdvanceInterruptibly(int phase)
throws InterruptedException {
final Phaser root = this.root;
long s = (root == this) ? state : reconcileState();
int p = (int)(s >>> PHASE_SHIFT);
if (phase < 0)
return phase;
if (p == phase) {
QNode node = new QNode(this, phase, true, false, 0L);
p = root.internalAwaitAdvance(phase, node);
if (node.wasInterrupted)
throw new InterruptedException();
}
return p;
}
說明: awaitAdvance
用于阻塞等待線程到達,直到phase前進到下一代,返回下一代的phase number。方法很簡單,不多贅述。awaitAdvanceInterruptibly
方法是響應中斷版的awaitAdvance,不同之處在于,調用阻塞時會記錄線程的中斷狀態。
小結
在使用上,Phaser可以實現CyclicBarrier和CountDownLatch類似的功能,而且它支持對任務的動態調整,并支持分層結構來達到更高的吞吐量。