CountDownLatch(倒計數器) - 人去樓空:人都走沒了,才能鎖門
作用:一個線程(或者多個), 等待另外N個線程完成某個事情之後才能執行。
實例:學生放學了,老師等所有學生都離開後,最後離開並鎖門(走一個減1,直到走完爲0)
代碼:
public class CountDownLatchDemo1 {
private static int COUNT = 8; // 該教室有8個學生
public static void main(String[] args) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(COUNT);
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < COUNT; i++) {
executorService.submit(new GotoMeat(String.valueOf(i), latch));
}
latch.await();
executorService.shutdown();
System.out.println("學生都走了,老師可以鎖門了!");
}
public static class GotoMeat implements Runnable{
private String name;
private CountDownLatch latch;
public GotoMeat(String name, CountDownLatch latch) {
this.name = name;
this.latch = latch;
}
@Override
public void run() {
System.out.println("學生 " + name + " 已經離開... ");
latch.countDown();
}
}
}
Semaphore(信號量,限流) - 食堂打飯:只有3個窗口可以同時打飯
作用:Semaphore用於限制可以訪問某些資源(物理或邏輯的)的線程數目,他維護了一個許可證集合,有多少資源需要限制就維護多少許可證集合,假如這裏有N個資源,那就對應於N個許可證,同一時刻也只能有N個線程訪問。一個線程獲取許可證就調用acquire方法,用完了釋放資源就調用release方法。
實例:高速路收費站有5個收費口,不管後面有多少輛車,每次只能通行5輛
(圖片來自網絡)
代碼:
public class SemaphoreDemo1 {
private static int COUNT = 5; // 共有5輛車等待通過收費口
private static int LIMIT = 2; // 有2個收費口
public static void main(String[] args) {
Semaphore semaphore = new Semaphore(LIMIT);
ExecutorService executorService = Executors.newCachedThreadPool();
for (int i = 0; i < COUNT; i++) {
executorService.submit(new PassThroughTheToll(String.valueOf(i), semaphore));
}
executorService.shutdown();
}
public static class PassThroughTheToll implements Runnable {
private String name;
private Semaphore semaphore;
public PassThroughTheToll(String name, Semaphore semaphore) {
this.name = name;
this.semaphore = semaphore;
}
@Override
public void run() {
try {
semaphore.acquire();
System.out.println("車輛 " + name + "拿到了通過收費站的許可...");
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
System.out.println("車輛 " + name + "通過收費站,釋放了許可~~~");
semaphore.release();
}
}
}
}
CyclicBarrier(屏障) - 運動員互相等待就位:全就位後,裁判發槍
作用:CyclicBarrier允許一組線程在到達某個柵欄點(common barrier point)互相等待,直到最後一個線程到達柵欄點,柵欄纔會打開,處於阻塞狀態的線程恢復繼續執行。
實例:師徒四人,都準備好了(到達公共屏障點common barrier point,相當於一個障礙點),再一起走(在障礙點一個都不能少,到齊後才能一起幹大事)
實例2:田徑比賽,各位運動員都準備好就位之後,裁判發槍
(圖片來源互聯網)
代碼:
public class CyclicBarrierDemo1 {
private static int COUNT = 4; // 師徒四人
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(COUNT);
String[] names = {"唐老闆", "猴哥", "八戒", "沙和尚"};
ExecutorService executorService = Executors.newFixedThreadPool(COUNT);
for (int i = 0; i < COUNT; i++) {
executorService.submit(new Travel(names[i], cyclicBarrier));
}
executorService.shutdown();
}
static class Travel implements Runnable {
private String name;
private CyclicBarrier cyclicBarrier;
public Travel(String name, CyclicBarrier cyclicBarrier) {
this.name = name;
this.cyclicBarrier = cyclicBarrier;
}
@Override
public void run() {
try {
TimeUnit.SECONDS.sleep(1+(new Random().nextInt(3)));
System.out.println(name + "已就位...,等待其他成員到來");
cyclicBarrier.await();
System.out.println(name + " :看到所有成員已到齊,四人一起過流沙河");
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
}
}
}
CountDownLatch 與 CyclicBarrier 的區別
CountDownLatch | CyclicBarrier |
---|---|
減計數方式 | 加計數方式 |
計數爲0時,釋放所等有待的線程 | 計數達到指定值時,釋放所有等待的線程 |
計數爲0時,無法重置 | 計數達到指定值時,計數置爲0,重新開始 |
調用countDown()方法減1 調用await()方法只進行阻塞,對計數沒任何影響 |
await()方法計數加1, 若加1後的值不等於初始指定的值,則線程阻塞 |
不可重複利用 | 可重複利用 |
CountDownLatch是計數器,線程完成一個記錄一個,只不過計數不是遞增而是遞減;
CyclicBarrier更像是一個閥門,需要所有線程都要到達,閥門才能打開,然後繼續執行。