Java為我們提供了一些同步輔助類
悯嗓,利用這些輔助類我們可以在多線程編程中瓷叫,靈活地把握線程的狀態(tài)妄辩。
CountDownLatch
CountDownLatch
一個同步輔助類
春瞬,在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個或多個線程一直等待鸭津。
再CountDownLatch中兩個比較關鍵的方法:
public void await() throws InterruptedException;
public void countDown();
CountDownLatch
是一個計數(shù)器
彤侍,它的構造方法中需要設置一個數(shù)值,用來設定計數(shù)的次數(shù)逆趋。每次調(diào)用countDown()
方法之后盏阶,這個計數(shù)器都會減去1
,CountDownLatch會一直阻塞著調(diào)用await()
方法的線程闻书,直到計數(shù)器
的值變?yōu)?code>0名斟。
設想有這樣一個功能需要Thread1、Thread2魄眉、Thread3砰盐、Thread4四條線程分別統(tǒng)計C、D坑律、E岩梳、F四個盤的大小,所有線程都統(tǒng)計完畢交給主線程去做匯總,利用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時完成磁盤的統(tǒng)計任務,耗費%d秒.\n", new Date().toString(), timer);
// 任務完成之后,計數(shù)器減一
count.countDown();
} catch (InterruptedException e) {
e.printStackTrace();
}
});
}
// 主線程一直被阻塞,知道count的計數(shù)器被設置為0
count.await();
System.out.printf("%s時全部任務都完成,執(zhí)行合并計算.\n", new Date().toString());
service.shutdown();
}
}
CyclicBarrier
Barrier
在英語中是屏障的意思淘捡,這個同步工具會阻塞調(diào)用的線程藕各,直到條件滿足時池摧,阻塞的線程同時被打開。
public int await() throws InterruptedException, BrokenBarrierException
CyclicBarrier
初始化的時候激况,設置一個屏障數(shù)作彤。線程調(diào)用await()
方法的時候,這個線程就會被阻塞乌逐,當調(diào)用await()
的線程數(shù)量到達屏障數(shù)的時候竭讳,主線程就會取消所有被阻塞線程的狀態(tài)。
在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
是可以被循環(huán)使用的,而且遇到線程中斷等情況時焰扳,還可以利用reset()
方法倦零,重置計數(shù)器,從這些方面來說吨悍,CyclicBarrier
會比CountDownLatch
更加靈活一些扫茅。
Semaphore
Semaphore
被用于控制特定資源在同一個時間被訪問的個數(shù)。類似連接池的概念育瓜,保證資源可以被合理的使用葫隙。
Semaphore
的幾個重要方法:
// 獲取資源
public void acquire() throws InterruptedException
// 釋放資源
public void release()
Semaphore
的構造方法可以設置一個int值
來設置一個計數(shù)器,用于表示資源同時可以被多少外部環(huán)境使用爆雹。每使用一次acquire()
停蕉,計數(shù)器都會去減去一,而每次調(diào)用release()
計數(shù)器則會增加一钙态。當計數(shù)器的值為0的時候慧起,外部的環(huán)境被阻塞,直到Semaphore
有空閑的資源可以被使用册倒。
public class SemaphoreTest {
private static Semaphore semaphore = new Semaphore(3);
private static ExecutorService service = Executors.newFixedThreadPool(6);
public static void main(String args[]) {
// 執(zhí)行9個任務
for (int i = 0; i < 9; i++) {
service.execute(() -> {
try {
semaphore.acquire();
System.out.printf("%s時獲取資源,并調(diào)用.\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時獲取資源,并調(diào)用.
Sun Mar 27 20:18:16 CST 2016時獲取資源,并調(diào)用.
Sun Mar 27 20:18:16 CST 2016時獲取資源,并調(diào)用.
Sun Mar 27 20:18:19 CST 2016時獲取資源,并調(diào)用.
Sun Mar 27 20:18:19 CST 2016時獲取資源,并調(diào)用.
Sun Mar 27 20:18:19 CST 2016時獲取資源,并調(diào)用.
Sun Mar 27 20:18:22 CST 2016時獲取資源,并調(diào)用.
Sun Mar 27 20:18:22 CST 2016時獲取資源,并調(diào)用.
Sun Mar 27 20:18:22 CST 2016時獲取資源,并調(diào)用.
雖然線程池允許6個最大線程數(shù)量蚓挤,但是同一個時間內(nèi)只用三個任務被執(zhí)行。