問題
最近研究比特幣, 從網(wǎng)絡(luò)上下載區(qū)塊的時(shí)候有時(shí)候碰到一個(gè)問題: 比特幣的網(wǎng)絡(luò)的請(qǐng)求和響應(yīng)式是異步, 發(fā)送了一個(gè)請(qǐng)求無法判斷這個(gè)請(qǐng)求有沒有響應(yīng), 為了解決這個(gè)問題, 使用超時(shí)機(jī)制. 如果一起請(qǐng)求發(fā)起后, 一段時(shí)間沒有接受到響應(yīng), 就切換到另外一個(gè)服務(wù)器重新請(qǐng)求, 于是就實(shí)現(xiàn)了一個(gè)工具類 TimeoutTaskManager
實(shí)現(xiàn)說明
通過一個(gè)死循環(huán)的線程while (true) { ... }
來監(jiān)聽超時(shí), 如果超時(shí)則切換到下一個(gè)服務(wù)器, 如果該線程被中斷會(huì)直接退出
執(zhí)行過程
假如有一個(gè)任務(wù)(Consumer<T> consumer
), 有N臺(tái)服務(wù)器可以支持(public void addProvider(T provider) {
), 當(dāng)前正在執(zhí)行的服務(wù)如果出現(xiàn)超時(shí)的時(shí)候(int time, TimeUnit unit
), 自動(dòng)按照隊(duì)列(Queue<T> queue
) 順序嘗試, 如果隊(duì)列里面沒有支持者則會(huì)一直等待 (waiting.await();
), 直到 addProvider(T provider)
的時(shí)候 waiting.signal()
喚醒.
- 構(gòu)造函數(shù)指定 任務(wù)和超時(shí)時(shí)間
public TimeoutTaskManager(Consumer<T> consumer, int time, TimeUnit unit) { ... }
- 添加服務(wù)的提供者
public void addProvider(T provider) { ... }
- 移除提供者
public synchronized void remove(T e) { ... }
- 如果是指定的任務(wù)是 幾個(gè)任務(wù)的合集, 例如:集合中的任務(wù)耗時(shí)不同, 可以在完成一個(gè)任務(wù)后重置任務(wù)的超時(shí)時(shí)間, 所有的操作都必須是當(dāng)前的提供者才可以修改, 因此有個(gè)參數(shù)
T me
, 返回值判斷是否修改成功
public synchronized boolean touch(T me, long time, TimeUnit unit) { ... }
另外有個(gè)幾個(gè)簡單方法, 分別是默認(rèn)超時(shí)時(shí)間的10倍和20倍: touchTenFold
, touchTwentyFold
- 有一個(gè)斷言方法認(rèn)定當(dāng)前是自己
assertIsMe
, 如果不是自己會(huì)拋出IllegalStateException
的異常
public TimeoutTaskManager<T> assertIsMe(T me, String format, Object... args) throws IllegalStateException { ... }
一些其他方法有用的方法
- 可以添加一個(gè)提供者切換時(shí)候的監(jiān)聽器, 有時(shí)候切換的時(shí)候需要做一些清理工作
public TimeoutTaskManager<T> addChangeListeners(BiConsumer<T, T> cn) { ... }
- 如果任務(wù)有共享數(shù)據(jù)可以放置到緩存中, 注意線程安全需要自己保證
public TimeoutTaskManager<T> putCache(String key, Object cache) { ... }
- 還提供了線程的同步的方法, 不會(huì)在
doing
的過程中產(chǎn)生provider
的切換, 注意:不要產(chǎn)生死鎖
public synchronized TimeoutTaskManager<T> ifMe(T me, Runnable doing) throws IllegalStateException { ... }
詳細(xì)代碼在 Gitee
TimeoutTaskManager
(完)