CountDownLatch:
可以讓一個執行緒等待其他執行緒完成了各自的工作之後再執行。比如說一個切菜,一個人切肉,都準備完畢之後才能炒肉。
構造方法:
public CountDownLatch(int count) count等待的執行緒數量
關鍵API:
countDown() 分執行緒執行完減少計數
await() 主執行緒等待呼叫
使用:
package com.nijunyang.concurrent; import java.util.concurrent.CountDownLatch; /** * Description: * Created by nijunyang on 2020/5/16 13:53 */ public class CountDownLatchTest{ private CountDownLatch countDownLatch; public CountDownLatchTest(CountDownLatch countDownLatch) { this.countDownLatch = countDownLatch; } public static void main(String[] args) throws InterruptedException { CountDownLatch countDownLatch = new CountDownLatch(2); CountDownLatchTest countDownLatchTest = new CountDownLatchTest(countDownLatch); new Thread(()-> { try { countDownLatchTest.method1(); } catch (InterruptedException e) { e.printStackTrace(); } },"執行緒1").start(); new Thread(()-> { try { countDownLatchTest.method2(); } catch (InterruptedException e) { e.printStackTrace(); } },"執行緒2").start(); System.out.println("等待食材準備完畢..."); countDownLatch.await(); System.out.println("炒肉..."); // System.out.println("------第二次使用-----"); // new Thread(()-> { // try { // countDownLatchTest.method1(); // } catch (InterruptedException e) { // e.printStackTrace(); // } // },"執行緒1").start(); // new Thread(()-> { // try { // countDownLatchTest.method2(); // } catch (InterruptedException e) { // e.printStackTrace(); // } // },"執行緒2").start(); // // System.out.println("等待食材準備完畢..."); // countDownLatch.await(); // System.out.println("炒肉..."); } private void method1() throws InterruptedException { Thread.sleep(5000L); System.out.println("切菜完畢..."); countDownLatch.countDown(); } private void method2() throws InterruptedException { Thread.sleep(10000L); System.out.println("切肉完畢..."); countDownLatch.countDown(); } }
原理解析:
1.從構造方法進去我們可以看到又是一個熟悉的Sync內部類繼承了AbstractQueuedSynchronizer,入參的數量被賦值到AbstractQueuedSynchronizer的state欄位。
2.await方法會去判斷state是否等於0,如果不等於0,說明其他執行緒還沒有執行完畢。就會執行doAcquireSharedInterruptibly這個方法,將當前這個呼叫await方法的執行緒入隊阻塞。
(呼叫鏈:await()-sync.acquireSharedInterruptibly-sync.tryAcquireShared-doAcquireSharedInterruptibly)
3.countDown方法,每調一次就會將state的值減1,當扣減到0的時候去喚醒上面等待的主執行緒執行(呼叫鏈:countDown-sync.releaseShared-sync.tryReleaseShared-doReleaseShared(減到0才會執行這方法))
CyclicBarrier:
籬柵,顧名思義有攔截作用。它可以讓一組執行緒到達柵欄時被阻塞,直到最後一個執行緒到達,才放行通過。比如玩LOL,需要等待所有玩家進度條100%了,才能進入遊戲
構造方法:
CyclicBarrier(int parties) parties:阻塞的執行緒數量
CyclicBarrier(int parties, Runnable barrierAction) parties:阻塞的執行緒數量 barrierAction:當最後一個執行緒到達是先執行這個任務,再去執行後面的流程。
關鍵API:
await() 到達柵欄點等待。呼叫次數要和入引數量一致,否則會一致阻塞的等待。
使用
package com.nijunyang.concurrent; import java.util.concurrent.CyclicBarrier; /** * @author: create by nijunyang * @date:2019/9/5 */ public class CyclicBarrierTest implements Runnable{ private CyclicBarrier cyclicBarrier; public CyclicBarrierTest(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } public void run() { try { System.out.println(Thread.currentThread().getName() + "進度條100%... "); cyclicBarrier.await(); } catch (Exception e) { e.printStackTrace(); } } public static void main(String[] args) throws Exception { CyclicBarrier cyclicBarrier = new CyclicBarrier(11, new Runnable() {//11個是因為還有一個主執行緒也在等待 public void run() { System.out.println("所有人進度條100%,準備開始遊戲"); } }); for (int i = 0; i < 10; i++) { new Thread(new CyclicBarrierTest(cyclicBarrier), "執行緒" + i).start(); } cyclicBarrier.await(); Thread.sleep(300); System.out.println("開始遊戲...."); } }
原理解析:
1.預設每個CyclicBarrier物件有一把鎖ReentrantLock和Condition
2.將構造方法的入引數量賦值到count欄位中。後續都是在count欄位上面進行操作。
3.await的呼叫會將count的數量-1,如果扣減到0.則會先執行構造方法傳入的任務(如果傳了),並且重置計數器重新整理柵欄,將許可資料重新賦值給count欄位(可以重複使用),喚醒條件等待的執行緒
4.如果扣減完成之後還沒有到0.說明還有執行緒沒有到達柵欄點。則進入條件佇列阻塞等到,等到最後一個到達時候,才被喚醒
兩者比較:
CountDownLatch和CyclicBarrier,最終實現效果看起來都差不多,都是等待分支執行緒執行完畢,再往下執行。然後CyclicBarrier這個可以重複使用,因為會去重新整理count的數量。CountDownLatch不會重新重新整理state欄位的值。當第二次await執行的時候一看state是0就直接放行了,所以一個CountDownLatch物件只能使用一次。
原理上CountDownLatch是阻塞主執行緒,分支線執行緒執行完畢將state扣減到0了之後喚醒主執行緒去執行,CyclicBarrier則是所有執行緒到達柵欄點都會阻塞等待。直到後一個到達才喚醒所有的阻塞執行緒。