最近把《java并發程式設計實戰》-Java Consurrency in Practice 重溫了一遍,把書中提到的一些常用工具記錄于此:
一、閉鎖(門栓)- CountDownLatch
适用場景:多線程測試時,通常為了精确計時,要求所有線程都ready後,才開始執行,防止有線程先起跑,造成不公平,類似的,所有線程執行完,整個程式才算運作完成。
執行結果:
線程-1 is running...
線程-5 is running...
線程-8 is running...
線程-4 is running...
線程-3 is running...
線程-0 is running...
線程-2 is running...
線程-9 is running...
線程-7 is running...
線程-6 is running...
done! exec time => 13 ms
注:大家可以把第14行注釋掉,再看看運作結果有什麼不同。
二、信号量(Semaphore)
适用場景:用于資源數有限制的并發通路場景。
上面的示例将一個普通的Set變成了有界容器。執行結果如下:
0 added !
1 added !
2 added !
3 added !
4 added !
5 not add to Set!
三、栅欄CyclicBarrier
這個跟閉鎖類似,可以通過代碼設定一個『屏障』點,其它線程到達該點後才能繼續,常用于限制其它線程都到達某一狀态後,才允許做後面的事情。
這裡我們假設有一個worder線程,裡面有2步操作,要求所有線程完成step1後,才能繼續step2. 執行結果如下:
Thread-0 step 1 ...
Thread-1 step 1 ...
Thread-2 step 1 ...
Thread-3 step 1 ...
Thread-4 step 1 ...
Thread-5 step 1 ...
Thread-6 step 1 ...
Thread-7 step 1 ...
Thread-8 step 1 ...
Thread-9 step 1 ...
Thread-9 step 2 ...
Thread-0 step 2 ...
Thread-3 step 2 ...
Thread-4 step 2 ...
Thread-6 step 2 ...
Thread-2 step 2 ...
Thread-1 step 2 ...
Thread-8 step 2 ...
Thread-7 step 2 ...
Thread-5 step 2 ...
四、Exchanger
如果2個線程需要交換資料,Exchanger就能派上用場了,見下面的示例:
執行結果:
thread 1 交換前:AAAAAA
thread 2 交換前:BBBBBB
thread 2 交換後:AAAAAA
thread 1 交換後:BBBBBB
五、FutureTask/Future
一些很耗時的操作,可以用Future轉化成異步,不阻塞後續的處理,直到真正需要傳回結果時調用get拿到結果
就緒。。。
主線程其它處理。。。
很耗時的操作進行中。。。
done
處理完成!
-----------------
executor 就緒。。。
六、阻塞隊列BlockingQueue
阻塞隊列可以線上程間實作生産者-消費者模式。比如下面的示例:線程producer模拟快速生産資料,而線程consumer模拟慢速消費資料,當達到隊列的上限時(即:生産者産生的資料,已經放不下了),隊列就堵塞住了。
producer 1 産生了一個數字:6773
consumer 1 消費了一個數字:6773
producer 1 産生了一個數字:4456
producer 1 産生了一個數字:8572
producer 1 産生了一個數字:5764
producer 1 産生了一個數字:2874
producer 1 産生了一個數字:780 # 注意這裡就已經堵住了,直到有消費者消費一條資料,才能繼續生産
consumer 1 消費了一個數字:4456
producer 1 産生了一個數字:4193