Phaser
Phaser

Phaser介紹
Phaser這個單詞是“移相器,相位器”的意思(好吧,筆者并不懂這是什么玩意,下方資料來自百度百科)。這個類是從JDK 1.7 中出現的。
移相器(Phaser)能夠對波的相位進行調整的一種裝置。任何傳輸介質對在其中傳導的波動都會引入相移,這是早期模擬移相器的原理;現代電子技術發展后利用A/D、D/A轉換實現了數字移相,顧名思義,它是一種不連續的移相技術,但特點是移相精度高。 移相器在雷達、導彈姿態控制、加速器、通信、儀器儀表甚至于音樂等領域都有著廣泛的應用
Phaser類有點復雜,這里只介紹一些基本的用法和知識點。詳情可以查看JDK文檔,文檔里有這個類非常詳盡的介紹。
前面我們介紹了CyclicBarrier,可以發現它在構造方法里傳入“任務總量”parties之后,就不能修改這個值了,并且每次調用await()方法也只能消耗一個parties計數。但Phaser可以動態地調整任務總量!
名詞解釋:
party:對應一個線程,數量可以通過register或者構造參數傳入;
arrive:對應一個party的狀態,初始時是unarrived,當調用arriveAndAwaitAdvance()或者?arriveAndDeregister()進入arrive狀態,可以通過getUnarrivedParties()獲取當前未到達的數量;
register:注冊一個party,每一階段必須所有注冊的party都到達才能進入下一階段;
deRegister:減少一個party。
phase:階段,當所有注冊的party都arrive之后,將會調用Phaser的onAdvance()方法來判斷是否要進入下一階段。
Phaser終止的兩種途徑,Phaser維護的線程執行完畢或者onAdvance()返回true?此外Phaser還能維護一個樹狀的層級關系,構造的時候new Phaser(parentPhaser),對于Task執行時間短的場景(競爭激烈),也就是說有大量的party, 那可以把每個Phaser的任務量設置較小,多個Phaser共同繼承一個父Phaser。
Phasers with large numbers of parties that would otherwise experience heavy synchronization contention costs may instead be set up so that groups of sub-phasers share a common parent. This may greatly increase throughput even though it incurs greater per-operation overhead.
翻譯:如果有大量的party,那許多線程可能同步的競爭成本比較高。所以可以拆分成多個子Phaser共享一個共同的父Phaser。這可能會大大增加吞吐量,即使它會帶來更多的每次操作開銷。
Phaser案例
還是游戲的案例。假設我們游戲有三個關卡,但只有第一個關卡有新手教程,需要加載新手教程模塊。但后面的第二個關卡和第三個關卡都不需要。我們可以用Phaser來做這個需求。
代碼:
public class PhaserDemo { static class PreTaskThread implements Runnable { private String task; private Phaser phaser; public PreTaskThread(String task, Phaser phaser) { this.task = task; this.phaser = phaser; } @Override public void run() { for (int i = 1; i < 4; i++) { try { // 第二次關卡起不加載NPC,跳過 if (i >= 2 && "加載新手教程".equals(task)) { continue; } Random random = new Random(); Thread.sleep(random.nextInt(1000)); System.out.println(String.format("關卡%d,需要加載%d個模塊,當前模塊【%s】", i, phaser.getRegisteredParties(), task)); // 從第二個關卡起,不加載NPC if (i == 1 && "加載新手教程".equals(task)) { System.out.println("下次關卡移除加載【新手教程】模塊"); phaser.arriveAndDeregister(); // 移除一個模塊 } else { phaser.arriveAndAwaitAdvance(); } } catch (InterruptedException e) { e.printStackTrace(); } } } } public static void main(String[] args) { Phaser phaser = new Phaser(4) { @Override protected boolean onAdvance(int phase, int registeredParties) { System.out.println(String.format("第%d次關卡準備完成", phase + 1)); return phase == 3 || registeredParties == 0; } }; new Thread(new PreTaskThread("加載地圖數據", phaser)).start(); new Thread(new PreTaskThread("加載人物模型", phaser)).start(); new Thread(new PreTaskThread("加載背景音樂", phaser)).start(); new Thread(new PreTaskThread("加載新手教程", phaser)).start(); } }
輸出:
關卡1,需要加載4個模塊,當前模塊【加載背景音樂】
關卡1,需要加載4個模塊,當前模塊【加載新手教程】
下次關卡移除加載【新手教程】模塊
關卡1,需要加載3個模塊,當前模塊【加載地圖數據】
關卡1,需要加載3個模塊,當前模塊【加載人物模型】
第1次關卡準備完成
關卡2,需要加載3個模塊,當前模塊【加載地圖數據】
關卡2,需要加載3個模塊,當前模塊【加載背景音樂】
關卡2,需要加載3個模塊,當前模塊【加載人物模型】
第2次關卡準備完成
關卡3,需要加載3個模塊,當前模塊【加載人物模型】
關卡3,需要加載3個模塊,當前模塊【加載地圖數據】
關卡3,需要加載3個模塊,當前模塊【加載背景音樂】
第3次關卡準備完成
這里要注意關卡1的輸出,在“加載新手教程”線程中調用了arriveAndDeregister()減少一個party之后,后面的線程使用getRegisteredParties()得到的是已經被修改后的parties了。但是當前這個階段(phase),仍然是需要4個parties都arrive才觸發屏障的。從下一個階段開始,才需要3個parties都arrive就觸發屏障。
另外Phaser類用來控制某個階段的線程數量很有用,但它并在意這個階段具體有哪些線程arrive,只要達到它當前階段的parties值,就觸發屏障。所以我這里的案例雖然制定了特定的線程(加載新手教程)來更直觀地表述Phaser的功能,但是其實Phaser是沒有分辨具體是哪個線程的功能的,它在意的只是數量,這一點需要讀者注意。
Phaser原理
Phaser類的原理相比起來要復雜得多。它內部使用了兩個基于Fork-Join框架的原子類輔助:
private final AtomicReference
有興趣的讀者可以去看看JDK源代碼,這里不做過多敘述。
總的來說,CountDownLatch,CyclicBarrier,Phaser是一個比一個強大,但也一個比一個復雜。根據自己的業務需求合理選擇即可。
5G游戲 任務調度
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。
版權聲明:本文內容由網絡用戶投稿,版權歸原作者所有,本站不擁有其著作權,亦不承擔相應法律責任。如果您發現本站中有涉嫌抄襲或描述失實的內容,請聯系我們jiasou666@gmail.com 處理,核實后本網站將在24小時內刪除侵權內容。