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則是所有線程到達柵欄點都會阻塞等待。直到後一個到達才喚醒所有的阻塞線程。