轉自 http://hrps.me/2016/11/22/java-concurrent-phaser/
Java7引入一種稱為Phaser的靈活的線程同步機制。如果你需要所有線程到達之后,才繼續或者開始進行新的一組任務,Phaser是一個很好的選擇。
下面是代碼,及每一步的解釋。
感覺原代碼解釋的不好,我從新寫了個感覺更好理解。??
import java.util.concurrent.Phaser;
/**
* Description:
* User: Huang rp
* Date: 16/11/23
* Version: 1.0
*/
public class PhaserSamples {
public static void main(String[] args) throws InterruptedException {
new PhaserSamples().runTasks();
}
// 一共開幾場會
private static final int PHASE_TO_TERMINATE = 2;
// 初始參會人員數量
private static final int INIT_PARTIES = 1;
// 增加參會人員數量
private static final int ADD_PARTIES = 5;
// 每個會場限制參會者數量
private static final int TASKS_PER_PHASER = 10;
void runTasks() throws InterruptedException {
final Phaser phaser = new Phaser(INIT_PARTIES) {
// 所有人員到達完畢,開始會議;連續開PHASE_TO_TERMINATE - 1 場會議,會議結束(terminal)
@Override
protected boolean onAdvance(int phase, int registeredParties) {
System.out.println("第" + (phase + 1) + "場會議結束");
return phase == (PHASE_TO_TERMINATE - 1) || registeredParties == 0;
}
};
final Task tasks[] = new Task[ADD_PARTIES];
System.out.println("會議開始準備,需要至少" + INIT_PARTIES + "人簽到, 共連續" + PHASE_TO_TERMINATE + "場會議,分會場數量" +
(ADD_PARTIES % TASKS_PER_PHASER == 0 ? (ADD_PARTIES / TASKS_PER_PHASER) : (ADD_PARTIES / TASKS_PER_PHASER + 1)));
System.out.println("Main準備參加第一場會議,開始簽到,共" + phaser.getRegisteredParties() + "人簽到");
build(tasks, 0, tasks.length, phaser);
for (int i = 0; i < tasks.length; i++) {
final Thread thread = new Thread(tasks[i]);
thread.start();
}
phaser.arriveAndDeregister();
System.out.println("Main離開會場");
// 準備下一場會議,如果沒有再到達會場,所有人都將等待
// phaser.arriveAndAwaitAdvance();
// System.out.println("Main休息準備下一場會議");
}
public static void build(Task[] tasks, int lo, int hi, Phaser ph) {
if (hi - lo > TASKS_PER_PHASER) {
for (int i = lo; i < hi; i += TASKS_PER_PHASER) {
int j = Math.min(i + TASKS_PER_PHASER, hi);
build(tasks, i, j, new Phaser(ph));
}
} else {
for (int i = lo; i < hi; ++i)
tasks[i] = new Task(i + 1, ph);
}
}
public static class Task implements Runnable {
private final int id;
private final Phaser phaser;
public Task(int id, Phaser phaser) {
this.id = id;
this.phaser = phaser;
this.phaser.register();
System.out.println("參會人員" + id + "已經簽到,共" + phaser.getRegisteredParties() + "人簽到");
}
@Override
public void run() {
// 參加每一場會議
while (!phaser.isTerminated()) {
try {
// 簽到之后,走到會場
Thread.sleep(20 * id + 20);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("參會人員" + this.id + "已經到達第" + (phaser.getPhase() + 1) + "場會議, 當前共有"
+ phaser.getRegisteredParties() + " 人參會,"
+ (phaser.getArrivedParties() + 1) + " 到達,"
+ (phaser.getUnarrivedParties() - 1) + " 未到達");
// 到達會場
phaser.arriveAndAwaitAdvance();
}
}
}
}
運行結果
會議開始準備,需要至少1人簽到, 共連續2場會議,分會場數量1
Main準備參加第一場會議,開始簽到,共1人簽到
參會人員1已經簽到,共2人簽到
參會人員2已經簽到,共3人簽到
參會人員3已經簽到,共4人簽到
參會人員4已經簽到,共5人簽到
參會人員5已經簽到,共6人簽到
Main離開會場
參會人員1已經到達第1場會議, 當前共有5 人參會,1 到達,4 未到達
參會人員2已經到達第1場會議, 當前共有5 人參會,2 到達,3 未到達
參會人員3已經到達第1場會議, 當前共有5 人參會,3 到達,2 未到達
參會人員4已經到達第1場會議, 當前共有5 人參會,4 到達,1 未到達
參會人員5已經到達第1場會議, 當前共有5 人參會,5 到達,0 未到達
第1場會議結束
參會人員1已經到達第2場會議, 當前共有5 人參會,1 到達,4 未到達
參會人員2已經到達第2場會議, 當前共有5 人參會,2 到達,3 未到達
參會人員3已經到達第2場會議, 當前共有5 人參會,3 到達,2 未到達
參會人員4已經到達第2場會議, 當前共有5 人參會,4 到達,1 未到達
參會人員5已經到達第2場會議, 當前共有5 人參會,5 到達,0 未到達
第2場會議結束
有幾點注意:
當簽到的人員數量等于到達會場人員數量,將立即開會,所以第一個人員到達會場會比最后一個簽到的晚才能保證所有人能參加會議。
可以一個會場容納所有人開會,將同一個phaser傳遞給所有的task;也可以維護一個層級關系,建立多個會場進行同一個會議,將所有參會人員平均分配到各個會場。
簽到之后,可以不用等待所有人到達會場可以立即離場,并且從簽到簿上抹掉簽名。
下一場會議開始之前,如果還有人員等待下一場會議,那么所有簽到的人都必須參加下一場。如果所有人都不等待,會議結束。
所有人員參加完指定數量的會議后,會議結束。
Phaser終止的兩種途徑,Phaser維護的線程執行完畢或者onAdvance()
返回true
此外Phaser還能維護一個樹狀的層級關系,構造的時候new Phaser(parentPhaser)
,對于Task執行時間短的場景(競爭激烈),** TASKS_PER_PHASER**值設置較小,反之適當增大。
名詞解釋:
-
party 對應一個線程,數量可以通過
register
或者初始化new Phaser(num)
的時候控制 -
arrive 對應一個party的狀態,初始時是unarrived,當調用
arriveAndAwaitAdvance()
或者arriveAndDeregister()
進入arrive狀態,可以通過getUnarrivedParties()
獲取當前未到達的數量 - register 注冊一個party,每一階段必須所有注冊的party都到達才能進入下一階段
-
phase 階段,當所有注冊的party都arrive之后,將會調用
Phaser
的onAdvance()
方法來判斷是否要進入下一階段