JAVA的幾個同步輔助類
Java為我們提供了一些同步輔助類
,利用這些輔助類我們可以在多執行緒程式設計中,靈活地把握執行緒的狀態。
CountDownLatch
CountDownLatch
一個同步輔助類
,在完成一組正在其他執行緒中執行的操作之前,它允許一個或多個執行緒一直等待。
再CountDownLatch中兩個比較關鍵的方法:
public void await() throws InterruptedException;
public void countDown();
CountDownLatch
是一個計數器
,它的構造方法中需要設定一個數值,用來設定計數的次數。每次呼叫countDown()
方法之後,這個計數器都會減去1
await()
方法的執行緒,直到計數器
的值變為0
。
設想有這樣一個功能需要Thread1、Thread2、Thread3、Thread4四條執行緒分別統計C、D、E、F四個盤的大小,所有執行緒都統計完畢交給主執行緒去做彙總,利用CountDownLatch來完成就非常輕鬆。
public class CountDownLatchTest {
private static CountDownLatch count = new CountDownLatch(4);
private static ExecutorService service = Executors.newFixedThreadPool(6 );
public static void main(String args[]) throws InterruptedException {
for (int i = 0; i < 4; i++) {
service.execute(() -> {
// 模擬任務耗時
try {
int timer = new Random().nextInt(5);
TimeUnit.SECONDS.sleep(timer);
System.out .printf("%s時完成磁碟的統計任務,耗費%d秒.\n", new Date().toString(), timer);
// 任務完成之後,計數器減一
count.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 主執行緒一直被阻塞,知道count的計數器被設定為0
count.await();
System.out.printf("%s時全部任務都完成,執行合併計算.\n", new Date().toString());
service.shutdown();
}
}
CyclicBarrier
Barrier
在英語中是屏障的意思,這個同步工具會阻塞呼叫的執行緒,直到條件滿足時,阻塞的執行緒同時被開啟。
public int await() throws InterruptedException, BrokenBarrierException
CyclicBarrier
初始化的時候,設定一個屏障數。執行緒呼叫await()
方法的時候,這個執行緒就會被阻塞,當呼叫await()
的執行緒數量到達屏障數的時候,主執行緒就會取消所有被阻塞執行緒的狀態。
在CyclicBarrier
的構造方法中,還可以設定一個barrierAction
。
在所有的屏障都到達之後,會啟動一個執行緒來執行這裡面的程式碼。這裡舉一個例子:百米賽跑的運動員起跑前需要準備,所有選手準備完畢之後,才可以同時起跑。
public class CyclicBarrierTest {
private static CyclicBarrier cyclicBarrier = new CyclicBarrier(8);
private static ExecutorService service = Executors.newFixedThreadPool(50);
public static void main(String args[]) {
for (int i = 1; i < 9; i++) {
service.execute(new Thread(new Runner(i, cyclicBarrier)));
}
service.shutdown();
}
}
// 運動員類
public class Runner implements Runnable {
private int number;
private CyclicBarrier cyclicBarrier;
public Runner(int number, CyclicBarrier cyclicBarrier) {
this.number = number;
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
int timer = new Random().nextInt(5);
TimeUnit.SECONDS.sleep(timer);
System.out.printf("%d號選手準備完畢,準備時間%d\n", number, timer);
cyclicBarrier.await();
System.out.printf("%d號選手於%s時起跑!\n", number, new Date().toString());
} catch (InterruptedException | BrokenBarrierException e) {
e.printStackTrace();
}
}
}
輸出:
1號選手準備完畢,準備時間0
4號選手準備完畢,準備時間0
5號選手準備完畢,準備時間1
8號選手準備完畢,準備時間1
3號選手準備完畢,準備時間2
2號選手準備完畢,準備時間3
7號選手準備完畢,準備時間3
6號選手準備完畢,準備時間3
7號選手於Sun Mar 27 21:19:00 CST 2016時起跑!
2號選手於Sun Mar 27 21:19:00 CST 2016時起跑!
5號選手於Sun Mar 27 21:19:00 CST 2016時起跑!
6號選手於Sun Mar 27 21:19:00 CST 2016時起跑!
3號選手於Sun Mar 27 21:19:00 CST 2016時起跑!
8號選手於Sun Mar 27 21:19:00 CST 2016時起跑!
4號選手於Sun Mar 27 21:19:00 CST 2016時起跑!
1號選手於Sun Mar 27 21:19:00 CST 2016時起跑!
相比CountDownLatch
,CyclicBarrier
是可以被迴圈使用的,而且遇到執行緒中斷等情況時,還可以利用reset()
方法,重置計數器,從這些方面來說,CyclicBarrier
會比CountDownLatch
更加靈活一些。
Semaphore
Semaphore
被用於控制特定資源在同一個時間被訪問的個數。類似連線池的概念,保證資源可以被合理的使用。
Semaphore
的幾個重要方法:
// 獲取資源
public void acquire() throws InterruptedException
// 釋放資源
public void release()
Semaphore
的構造方法可以設定一個int值
來設定一個計數器,用於表示資源同時可以被多少外部環境使用。每使用一次acquire()
,計數器都會去減去一,而每次呼叫release()
計數器則會增加一。當計數器的值為0的時候,外部的環境被阻塞,直到Semaphore
有空閒的資源可以被使用。
public class SemaphoreTest {
private static Semaphore semaphore = new Semaphore(3);
private static ExecutorService service = Executors.newFixedThreadPool(6);
public static void main(String args[]) {
// 執行9個任務
for (int i = 0; i < 9; i++) {
service.execute(() -> {
try {
semaphore.acquire();
System.out.printf("%s時獲取資源,並呼叫.\n", new Date().toString());
// 執行緒掛起3秒
TimeUnit.SECONDS.sleep(3);
semaphore.release();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
service.shutdown();
}
}
執行的結果就是:
Sun Mar 27 20:18:16 CST 2016時獲取資源,並呼叫.
Sun Mar 27 20:18:16 CST 2016時獲取資源,並呼叫.
Sun Mar 27 20:18:16 CST 2016時獲取資源,並呼叫.
Sun Mar 27 20:18:19 CST 2016時獲取資源,並呼叫.
Sun Mar 27 20:18:19 CST 2016時獲取資源,並呼叫.
Sun Mar 27 20:18:19 CST 2016時獲取資源,並呼叫.
Sun Mar 27 20:18:22 CST 2016時獲取資源,並呼叫.
Sun Mar 27 20:18:22 CST 2016時獲取資源,並呼叫.
Sun Mar 27 20:18:22 CST 2016時獲取資源,並呼叫.
雖然執行緒池允許6個最大執行緒數量,但是同一個時間內只用三個任務被執行。