Java線程并發(fā)工具類CountDownLatch原理及用法
一、CountDownLatch
【1】CountDownLatch是什么?
CountDownLatch,英文翻譯為倒計(jì)時(shí)鎖存器,是一個(gè)同步輔助類,在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個(gè)或
多個(gè)線程一直等待。
閉鎖可以延遲線程的進(jìn)度直到其到達(dá)終止?fàn)顟B(tài),閉鎖可以用來確保某些活動(dòng)直到其他活動(dòng)都完成才繼續(xù)執(zhí)行:
- 確保某個(gè)計(jì)算在其需要的所有資源都被初始化之后才繼續(xù)執(zhí)行;
- 確保某個(gè)服務(wù)在其依賴的所有其他服務(wù)都已經(jīng)啟動(dòng)之后才啟動(dòng);
- 等待直到某個(gè)操作所有參與者都準(zhǔn)備就緒再繼續(xù)執(zhí)行;
CountDownLatch有一個(gè)正數(shù)計(jì)數(shù)器,countDown()方法對(duì)計(jì)數(shù)器做減操作,await()方法等待計(jì)數(shù)器達(dá)到0。所有await的線程都會(huì)阻塞直到計(jì)數(shù)器為0或者等待線程中斷或者超時(shí)。
閉鎖(倒計(jì)時(shí)鎖)主要用來保證完成某個(gè)任務(wù)的先決條件滿足。是一個(gè)同步工具類,用來協(xié)調(diào)多個(gè)線程之間的同步。這個(gè)工具通常用來控制線程等待,它可以讓某一個(gè)線程等待直到倒計(jì)時(shí)結(jié)束,再開始執(zhí)行。
【2】CountDownLatch的兩種典型用法
①某一線程在開始運(yùn)行前等待n個(gè)線程執(zhí)行完畢。
將 CountDownLatch 的計(jì)數(shù)器初始化為n :new CountDownLatch(n),每當(dāng)一個(gè)任務(wù)線程執(zhí)行完畢,就將計(jì)數(shù)器減1 countdownlatch.countDown(),當(dāng)計(jì)數(shù)器的值變?yōu)?時(shí),在CountDownLatch上 await() 的線程就會(huì)被喚醒。一個(gè)典型應(yīng)用場(chǎng)景就是啟動(dòng)一個(gè)服務(wù)時(shí),主線程需要等待多個(gè)組件加載完畢,之后再繼續(xù)執(zhí)行。
②實(shí)現(xiàn)多個(gè)線程開始執(zhí)行任務(wù)的最大并行性。
注意是并行性,不是并發(fā),強(qiáng)調(diào)的是多個(gè)線程在某一時(shí)刻同時(shí)開始執(zhí)行。類似于賽跑,將多個(gè)線程放到起點(diǎn),等待發(fā)令槍響,然后同時(shí)開跑。做法是初始化一個(gè)共享的 CountDownLatch 對(duì)象,將其計(jì)數(shù)器初始化為 1 :new CountDownLatch(1),多個(gè)線程在開始執(zhí)行任務(wù)前首先 coundownlatch.await(),當(dāng)主線程調(diào)用 countDown() 時(shí),計(jì)數(shù)器變?yōu)?,多個(gè)線程同時(shí)被喚醒。
如下例所示,在多線程運(yùn)行的情況下,計(jì)算多線程耗費(fèi)的時(shí)間:
public class TestCountDownLatch { //CountDownLatch 為唯一的、共享的資源 static CountDownLatch countDownLatch = new CountDownLatch(5); static class LatchDemo extends Thread{ @Override public void run() { int sum = 0; for (int i = 0; i < 1000000; i++) { sum++; } System.out.println(getName()+"計(jì)算結(jié)果:"+sum); countDownLatch.countDown(); } } public static void main(String[] args) throws InterruptedException { long begin = System.currentTimeMillis(); System.out.println("開始了-----"+begin); for (int i = 0; i < 5; i++) { new LatchDemo().start(); } countDownLatch.await(); long end = System.currentTimeMillis(); System.out.println("結(jié)束了-----"+end); System.out.println("總共用時(shí):"+(end-begin)); } } /** 開始了-----1571144894551 Thread-3計(jì)算結(jié)果:1000000 Thread-0計(jì)算結(jié)果:1000000 Thread-1計(jì)算結(jié)果:1000000 Thread-2計(jì)算結(jié)果:1000000 Thread-4計(jì)算結(jié)果:1000000 結(jié)束了-----1571144894559 總共用時(shí):8 */
二、CyclicBarrier
【1】CyclicBarrier是什么?
CyclicBarrier即柵欄類,與CountDownLatch類似。它能阻塞一組線程直到某個(gè)事件的發(fā)生。柵欄與閉鎖的關(guān)鍵區(qū)別在于,所有的線程必須同時(shí)到達(dá)柵欄位置,才能繼續(xù)執(zhí)行。
CyclicBarrier可以使一定數(shù)量的線程反復(fù)地在柵欄位置處匯集。當(dāng)線程到達(dá)柵欄位置時(shí)將調(diào)用await方法,這個(gè)方法將阻塞直到所有線程都到達(dá)柵欄位置。如果所有線程都到達(dá)柵欄位置,那么柵欄將打開,此時(shí)所有的線程都將被釋放,而柵欄將被重置以便下次使用。
【2】CyclicBarrier構(gòu)造方法
public CyclicBarrier(int parties) { this(parties, null); } public CyclicBarrier(int parties, Runnable barrierAction) { if (parties <= 0) throw new IllegalArgumentException(); this.parties = parties; this.count = parties; this.barrierCommand = barrierAction; }
CyclicBarrier默認(rèn)的構(gòu)造方法是CyclicBarrier(int parties),其參數(shù)表示屏障攔截的線程數(shù)量,每個(gè)線程使用await()方法告訴CyclicBarrier我已經(jīng)到達(dá)了屏障,然后當(dāng)前線程被阻塞。
CyclicBarrier的另一個(gè)構(gòu)造函數(shù)CyclicBarrier(int parties, Runnable barrierAction),用于線程到達(dá)屏障時(shí),優(yōu)先執(zhí)行barrierAction,方便處理更復(fù)雜的業(yè)務(wù)場(chǎng)景。
【3】CyclicBarrier應(yīng)用示例
public class CyclicBarrierTest { // 自定義工作線程 private static class Worker extends Thread { private CyclicBarrier cyclicBarrier; public Worker(CyclicBarrier cyclicBarrier) { this.cyclicBarrier = cyclicBarrier; } @Override public void run() { super.run(); try { System.out.println(Thread.currentThread().getName() + "開始等待其他線程"); cyclicBarrier.await(); System.out.println(Thread.currentThread().getName() + "開始執(zhí)行"); // 工作線程開始處理,這里用Thread.sleep()來模擬業(yè)務(wù)處理 Thread.sleep(1000); System.out.println(Thread.currentThread().getName() + "執(zhí)行完畢"); } catch (Exception e) { e.printStackTrace(); } } } public static void main(String[] args) { int threadCount = 3; CyclicBarrier cyclicBarrier = new CyclicBarrier(threadCount); for (int i = 0; i < threadCount; i++) { System.out.println("創(chuàng)建工作線程" + i); Worker worker = new Worker(cyclicBarrier); worker.start(); } } } /** 創(chuàng)建工作線程0 創(chuàng)建工作線程1 Thread-0開始等待其他線程 創(chuàng)建工作線程2 Thread-1開始等待其他線程 Thread-2開始等待其他線程 Thread-2開始執(zhí)行 Thread-0開始執(zhí)行 Thread-1開始執(zhí)行 Thread-1執(zhí)行完畢 Thread-0執(zhí)行完畢 Thread-2執(zhí)行完畢 */
在上述代碼中,我們自定義的工作線程必須要等所有參與線程開始之后才可以執(zhí)行,我們可以使用CyclicBarrier類來幫助我們完成。從程序的執(zhí)行結(jié)果中也可以看出,所有的工作線程都運(yùn)行await()方法之后都到達(dá)了柵欄位置,然后,3個(gè)工作線程才開始執(zhí)行業(yè)務(wù)處理。
【4】CyclicBarrier和CountDownLatch的區(qū)別
- CountDownLatch的計(jì)數(shù)器只能使用一次,而CyclicBarrier的計(jì)數(shù)器可以使用reset()方法重置,可以使用多次,所以CyclicBarrier能夠處理更為復(fù)雜的場(chǎng)景;
- CyclicBarrier還提供了一些其他有用的方法,比如getNumberWaiting()方法可以獲得CyclicBarrier阻塞的線程數(shù)量,isBroken()方法用來了解阻塞的線程是否被中斷;
- CountDownLatch允許一個(gè)或多個(gè)線程等待一組事件的產(chǎn)生,而CyclicBarrier用于等待其他線程運(yùn)行到柵欄位置。
三、Semaphore
【1】Semaphore是什么?
信號(hào)量(Semaphore),又被稱為信號(hào)燈,在多線程環(huán)境下用于協(xié)調(diào)各個(gè)線程, 以保證它們能夠正確、合理的使用公共資源。信號(hào)量維護(hù)了一個(gè)許可集,我們?cè)诔跏蓟疭emaphore時(shí)需要為這個(gè)許可集傳入一個(gè)數(shù)量值,該數(shù)量值代表同一時(shí)間能訪問共享資源的線程數(shù)量。
【2】Semaphore基本用法
線程可以通過acquire()方法獲取到一個(gè)許可,然后對(duì)共享資源進(jìn)行操作,注意如果許可集已分配完了,那么線程將進(jìn)入等待狀態(tài),直到其他線程釋放許可才有機(jī)會(huì)再獲取許可,線程釋放一個(gè)許可通過release()方法完成,"許可"將被歸還給Semaphore。
【3】Semaphore實(shí)現(xiàn)互斥鎖
public class TestSemaphore { //初始化為1,互斥信號(hào)量 private final static Semaphore mutex = new Semaphore(1); static class thread extends Thread{ @Override public void run() { try { mutex.acquire(); System.out.println(getName()+"開始工作"); } catch (InterruptedException e) { e.printStackTrace(); }finally { //使用完成釋放鎖 mutex.release(); System.out.println("鎖釋放!!!"); } } } public static void main(String[] args) { for (int i = 0; i < 10; i++) { new Thread(new thread(),String.valueOf(i)).start(); } } }
創(chuàng)建一個(gè)數(shù)量為1的互斥信號(hào)量Semaphore,然后并發(fā)執(zhí)行10個(gè)線程,在線程中利用Semaphore控制線程的并發(fā)執(zhí)行,因?yàn)樾盘?hào)量數(shù)值只有1,因此每次只能一條線程執(zhí)行,其他線程進(jìn)入等待狀態(tài)。
四、Callable、Future和FutureTask
Future接口,一般都是取回Callable執(zhí)行的狀態(tài)用的。其中的主要方法:
- cancel,取消Callable的執(zhí)行,當(dāng)Callable還沒有完成時(shí)
- get,獲得Callable的返回值
- isCanceled,判斷是否取消了
- isDone,判斷是否完成
以上就是本文的全部?jī)?nèi)容,希望對(duì)大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。
相關(guān)文章
詳述IntelliJ IDEA提交代碼前的 Code Analysis 機(jī)制(小結(jié))
本篇文章主要介紹了詳述IntelliJ IDEA提交代碼前的 Code Analysis 機(jī)制(小結(jié)),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-11-11JavaWeb之Servlet注冊(cè)頁面的實(shí)現(xiàn)示例
注冊(cè)頁面是很多網(wǎng)站都會(huì)是使用的到,本文主要介紹了JavaWeb之Servlet注冊(cè)頁面的實(shí)現(xiàn)示例,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-04-04SpringBoot下使用定時(shí)任務(wù)的方式全揭秘(6種)
這篇文章主要介紹了SpringBoot下使用定時(shí)任務(wù)的方式全揭秘(6種),小編覺得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過來看看吧2019-02-02java實(shí)現(xiàn)Flappy Bird游戲源代碼
這篇文章主要為大家詳細(xì)介紹了java實(shí)現(xiàn)Flappy Bird游戲源代碼,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2018-12-12Java實(shí)現(xiàn)單機(jī)版五子棋游戲的示例代碼
五子棋是世界智力運(yùn)動(dòng)會(huì)競(jìng)技項(xiàng)目之一,是一種兩人對(duì)弈的純策略型棋類游戲,是世界智力運(yùn)動(dòng)會(huì)競(jìng)技項(xiàng)目之一。本文將用java語言實(shí)現(xiàn)單機(jī)版五子棋游戲,感興趣的可以了解一下2022-09-09SpringBoot集成netty實(shí)現(xiàn)websocket通信功能
Netty是一個(gè)高性能、異步事件驅(qū)動(dòng)的網(wǎng)絡(luò)應(yīng)用框架,用于快速開發(fā)可維護(hù)的高性能協(xié)議服務(wù)器和客戶端,WebSocket 是一種網(wǎng)絡(luò)通信協(xié)議,相比傳統(tǒng)的HTTP協(xié)議,本文給大家介紹了SpringBoot集成netty實(shí)現(xiàn)websocket通信功能,需要的朋友可以參考下2024-03-03SpringBoot使用Caffeine實(shí)現(xiàn)緩存的示例代碼
本文主要介紹了SpringBoot使用Caffeine實(shí)現(xiàn)緩存的示例代碼,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2022-07-07