常用輔助類(ContDownLatch、CyclicBarrier、Semaphore)
1.ContDownLatch(倒計時器)
ContDownLatch是一個同步輔助類,在完成某些運算時,只有其他所有執行緒的運算全部完成,當前運算才繼續執行,這就叫閉鎖。
● CountDownLatch(int count):例項化一個倒計數器,count指定計數個數
● countDown():計數減一
● await():等待,當計數減到0時,所有執行緒並行執行
程式碼演示:火箭發射
public class ConutDownLatchDemo { public static void main(String[] args) throws InterruptedException {// 倒計時器:計數數量為10 CountDownLatch latch = new CountDownLatch(10); // 倒計時檢查任務,使用10個執行緒來完成任務 CountRunnable runnable = new CountRunnable(latch); for (int i = 0; i < 10; i++) { new Thread(runnable).start(); // 執行任務 } // 等待檢查完成 latch.await();// 發射火箭 System.out.println("Fire!"); } } class CountRunnable implements Runnable { private CountDownLatch countDownLatch; public CountRunnable(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } @Override public void run() { // 模擬檢查任務try { Thread.sleep(new Random().nextInt(10) * 1000); System.out.println("check complete"); } catch (InterruptedException e) { e.printStackTrace(); } finally { // 計數 -1 // 放在finally避免任務執行過程出現異常,導致countDown()不能被執行 countDownLatch.countDown(); } } }
上述程式碼中我們先生成了一個CountDownLatch例項。計數數量為10,這表示需要有10個執行緒來完成任務,等待在CountDownLatch上的執行緒才能繼續執行。latch.countDown();方法作用是通知CountDownLatch有一個執行緒已經準備完畢,倒計數器可以減一了。latch.await()方法要求主執行緒等待所有10個檢查任務全部準備好才一起並行執行。
2.CyclicBarrier(加法計數器)
一個同步輔助類,它允許一組執行緒互相等待,直到到達某個公共屏障點 (common barrier point)。在涉及一組固定大小的執行緒的程式中,這些執行緒必須不時地互相等待,此時 CyclicBarrier 很有用。因為該 barrier 在釋放等待執行緒後可以重用,所以稱它為迴圈 的 barrier。
使用場景:
需要所有的子任務都完成時,才執行主任務,這個時候就可以選擇使用CyclicBarrier。
程式碼演示:集龍珠召喚神龍
public class CyclicBarrierDemo { public static void main(String[] args) { // 加法計數器,集齊7顆龍珠召喚神龍 CyclicBarrier cyclicBarrier = new CyclicBarrier(7, new Runnable() { @Override public void run() { System.out.println("集齊龍珠 -> 召喚神龍!嗷!嗷!嗷!!!"); } }); for (int i = 0; i < 7; i++) { new Thread(() -> { System.out.println("獲得(" + Thread.currentThread().getName() + ")星龍珠"); try { cyclicBarrier.await(); // 等待其它執行緒執行完成 } catch (InterruptedException e) { e.printStackTrace(); } catch (BrokenBarrierException e) { e.printStackTrace(); } }, String.valueOf(i + 1)).start(); } } }
◇ CountDownLatch 和 CyclicBarrier 比較
① CountDownLatch 是執行緒組之間的等待,即一個(或多個)執行緒等待N個執行緒完成某件事情之後再執行。
CyclicBarrier 則是執行緒組內的等待,即每個執行緒相互等待,即 N 個執行緒都被攔截之後,然後依次執行。
② CountDownLatch 是減計數方式。
CyclicBarrier 是加計數方式。
③ CountDownLatch 計數為 0 無法重置。
CyclicBarrier 計數達到初始值,則可以重置。
④ CountDownLatch 不可以複用,
CyclicBarrier 可以複用。
3.Semaphore(訊號量)
Semaphore 是一個計數訊號量,必須由獲取它的執行緒釋放。
作用:多個共享資源互斥的使用!併發限流,限制可以訪問某些資源的執行緒數量。
Semaphore 只有3個操作:① 初始化 ② 增加 ③ 減少
常用方法:
public void acquire();// 獲取一個許可 public void acquire(intpermits);// 獲取permits個許可 public void release();// 釋放一個許可 public void release(intpermits);// 釋放permits個許可
- acquire:用來獲取一個許可,若無許可能夠獲得,則會一直等待,直到獲得許可。
- release:用來釋放許可。注意,在釋放許可之前,必須先獲獲得許可。
這4個方法都會被阻塞,如果想立即得到執行結果,可以使用下面幾個方法:
// 嘗試獲取一個許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回false public boolean tryAcquire(); // 嘗試獲取一個許可,若在指定的時間內獲取成功,則立即返回true,否則則立即返回false public boolean tryAcquire(longtimeout, TimeUnit unit); // 嘗試獲取permits個許可,若獲取成功,則立即返回true,若獲取失敗,則立即返回false public boolean tryAcquire(intpermits); // 嘗試獲取permits個許可,若在指定的時間內獲取成功,則立即返回true,否則則立即返回false public boolean tryAcquire(intpermits, longtimeout, TimeUnit unit);
程式碼演示:搶車位
現有6輛車,但是隻有3個停車位
public class SemaphoreDemo { public static void main(String[] args) { // 訊號量,只允許 3 個執行緒同時訪問 Semaphore semaphore = new Semaphore(3); for (int i = 0; i < 6; i++) { new Thread(() -> { try { semaphore.acquire(); // 獲取許可,將訊號量 +1,如果訊號量已經滿了,等待被釋放為止! System.out.println("Car" + Thread.currentThread().getName() + " >>> 搶到車位"); TimeUnit.SECONDS.sleep(new Random().nextInt(10)); System.out.println("離開車位 >>> Car" + Thread.currentThread().getName()); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); // 釋放,將訊號量 -1,然後喚醒等待的執行緒! } }, String.valueOf(i)).start(); } } }
◇ 總結
CountDownLatch 是一個執行緒等待其他執行緒, CyclicBarrier 是多個執行緒互相等待。
CountDownLatch 的計數是減 1 直到 0,CyclicBarrier 是加 1,直到指定值。
CountDownLatch 是一次性的, CyclicBarrier 可以迴圈利用。
CyclicBarrier 可以在最後一個執行緒達到屏障之前,選擇先執行一個操作。
Semaphore ,需要拿到許可才能執行,並可以選擇公平和非公平模式。