CountDownLatch、CyclicBarrier和Semaphore
阿新 • • 發佈:2022-02-27
1、CountDownLatch用法
類似於計數器,比如某個任務需要等待另外N個任務執行完後再繼續執行,就可以用CountDownLatch實現。
構造方法:
//count為計數器值
public CountDownLatch(int count) { if (count < 0) throw new IllegalArgumentException("count < 0"); this.sync = new Sync(count); }
另外還有三個主要方法:
//呼叫該方法的執行緒會進入阻塞狀態,等到count值為0時才會繼續執行 public voidawait() throws InterruptedException { sync.acquireSharedInterruptibly(1); } //作用同上一個方法,只不過加了超時時間,即使count不為0但超過timeout後也會繼續執行 public boolean await(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } //將計數器count值減一public void countDown() { sync.releaseShared(1); }
Demo:比如五個工人幹活,工頭要等他們一起全部幹完活才結算工資
public class Demo { public static void main(String[] args) { CountDownLatch countDownLatch = new CountDownLatch(5); for (int i = 0; i < 5; i ++) { final int worker = i;new Thread(() -> { System.out.println("工人" + worker + "開始工作"); try{ //睡眠模擬幹活 Thread.sleep(worker * 1000); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("工人" + worker + "完成工作"); countDownLatch.countDown(); }).start(); } try { countDownLatch.await(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("所有工作已完成,工頭結算工資"); } }
列印結果:
工人0開始工作
工人1開始工作
工人0完成工作
工人4開始工作
工人2開始工作
工人3開始工作
工人1完成工作
工人2完成工作
工人3完成工作
工人4完成工作
所有工作已完成,工頭結算工資
View Code
2、CyclicBarrier
它是針對在某一組任務內相互等待,直到這一組任務都執行到某一個設定的點(呼叫await方法)之後再繼續各自執行後續操作。CyclicBarrier是可重用的,CountDownLatch則不行。
構造方法:
//構造引數為每組任務的任務數 public CyclicBarrier(int parties) { this(parties, null); } //barrierAction:可以在該組任務到達指定點後由最晚到達的執行緒執行額外的操作 public CyclicBarrier(int parties, Runnable barrierAction) { if (parties <= 0) throw new IllegalArgumentException(); this.parties = parties; this.count = parties; this.barrierCommand = barrierAction; }
關鍵方法:
//呼叫該方法後執行緒進入等待,直到該組所有執行緒都執行到這一句再開始後續操作 public int await() throws InterruptedException, BrokenBarrierException { try { return dowait(false, 0L); } catch (TimeoutException toe) { throw new Error(toe); // cannot happen } } //同上個方法,多加了一個超時時間,超過設定時間直接執行後續操作 public int await(long timeout, TimeUnit unit) throws InterruptedException, BrokenBarrierException, TimeoutException { return dowait(true, unit.toNanos(timeout)); }
Demo:比如體育課老師讓學生跑步,全部一起跑完才可以各自自由活動
public class Demo { public static void main(String[] args) { CyclicBarrier cyclicBarrier = new CyclicBarrier(3, () -> { System.out.println(Thread.currentThread().getName() + "告訴老師,所有人已完成跑步"); }); for (int i = 0; i < 3; i ++) { final int stu = i; new Thread(() -> { System.out.println("學生" + stu + "開始跑步"); try{ //睡眠模擬跑步 Thread.sleep(2000); System.out.println("學生" + stu + "已到達終點"); cyclicBarrier.await(); } catch (Exception e) { e.printStackTrace(); } System.out.println("所有人到達終點,學生" + stu + "自由活動"); }, "學生" + stu).start(); } } }
列印結果:
學生0開始跑步
學生1開始跑步
學生2開始跑步
學生2已到達終點
學生1已到達終點
學生0已到達終點
學生0告訴老師,所有人已完成跑步
所有人到達終點,學生0自由活動
所有人到達終點,學生1自由活動
所有人到達終點,學生2自由活動
View Code
3、Semaphore
訊號量,可以控制同時執行的執行緒數量,類似於鎖。
構造方法:
//permits為許可個數,預設為非公平的 public Semaphore(int permits) { sync = new NonfairSync(permits); } //fair表示是否公平的,公平的就是FIFO public Semaphore(int permits, boolean fair) { sync = fair ? new FairSync(permits) : new NonfairSync(permits); }
幾個關鍵方法:
//獲取一個許可,獲取不到時會進入阻塞等待 public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1); } //獲取permits個許可 public void acquire(int permits) throws InterruptedException { if (permits < 0) throw new IllegalArgumentException(); sync.acquireSharedInterruptibly(permits); } //釋放一個許可 public void release() { sync.releaseShared(1); } //釋放permits個許可 public void release(int permits) { if (permits < 0) throw new IllegalArgumentException(); sync.releaseShared(permits); }
Demo:銀行櫃檯每個服務視窗同一時間只能服務一個顧客,其他顧客只能等待
public class Demo { public static void main(String[] args) throws Exception { //三個服務視窗 Semaphore semaphore = new Semaphore(3); //9個顧客辦理業務 for (int i = 0; i < 9; i ++) { final int customer = i; new Thread(() -> { try{ semaphore.acquire(); System.out.println("顧客" + customer + "獲得許可,櫃檯開始服務"); //睡眠模擬櫃檯服務 Thread.sleep(2000); semaphore.release(); System.out.println("顧客" + customer + "業務已辦理完成,釋放許可"); } catch (Exception e) { e.printStackTrace(); } }).start(); } } }
列印結果:
顧客2獲得許可,櫃檯開始服務
顧客2業務已辦理完成,釋放許可
顧客0業務已辦理完成,釋放許可
顧客3獲得許可,櫃檯開始服務
顧客4業務已辦理完成,釋放許可
顧客1獲得許可,櫃檯開始服務
顧客5獲得許可,櫃檯開始服務
顧客1業務已辦理完成,釋放許可
顧客5業務已辦理完成,釋放許可
顧客6獲得許可,櫃檯開始服務
顧客7獲得許可,櫃檯開始服務
顧客3業務已辦理完成,釋放許可
顧客8獲得許可,櫃檯開始服務
顧客8業務已辦理完成,釋放許可
顧客6業務已辦理完成,釋放許可
顧客7業務已辦理完成,釋放許可
View Code