Java線程并發(fā)工具類CountDownLatch原理及用法
一、CountDownLatch
【1】CountDownLatch是什么?
CountDownLatch,英文翻譯為倒計時鎖存器,是一個同步輔助類,在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個或
多個線程一直等待。
閉鎖可以延遲線程的進度直到其到達終止狀態(tài),閉鎖可以用來確保某些活動直到其他活動都完成才繼續(xù)執(zhí)行:
- 確保某個計算在其需要的所有資源都被初始化之后才繼續(xù)執(zhí)行;
- 確保某個服務在其依賴的所有其他服務都已經(jīng)啟動之后才啟動;
- 等待直到某個操作所有參與者都準備就緒再繼續(xù)執(zhí)行;
CountDownLatch有一個正數(shù)計數(shù)器,countDown()方法對計數(shù)器做減操作,await()方法等待計數(shù)器達到0。所有await的線程都會阻塞直到計數(shù)器為0或者等待線程中斷或者超時。
閉鎖(倒計時鎖)主要用來保證完成某個任務的先決條件滿足。是一個同步工具類,用來協(xié)調多個線程之間的同步。這個工具通常用來控制線程等待,它可以讓某一個線程等待直到倒計時結束,再開始執(zhí)行。
【2】CountDownLatch的兩種典型用法
①某一線程在開始運行前等待n個線程執(zhí)行完畢。
將 CountDownLatch 的計數(shù)器初始化為n :new CountDownLatch(n),每當一個任務線程執(zhí)行完畢,就將計數(shù)器減1 countdownlatch.countDown(),當計數(shù)器的值變?yōu)?時,在CountDownLatch上 await() 的線程就會被喚醒。一個典型應用場景就是啟動一個服務時,主線程需要等待多個組件加載完畢,之后再繼續(xù)執(zhí)行。
②實現(xiàn)多個線程開始執(zhí)行任務的最大并行性。
注意是并行性,不是并發(fā),強調的是多個線程在某一時刻同時開始執(zhí)行。類似于賽跑,將多個線程放到起點,等待發(fā)令槍響,然后同時開跑。做法是初始化一個共享的 CountDownLatch 對象,將其計數(shù)器初始化為 1 :new CountDownLatch(1),多個線程在開始執(zhí)行任務前首先 coundownlatch.await(),當主線程調用 countDown() 時,計數(shù)器變?yōu)?,多個線程同時被喚醒。
如下例所示,在多線程運行的情況下,計算多線程耗費的時間:
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()+"計算結果:"+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("結束了-----"+end);
System.out.println("總共用時:"+(end-begin));
}
}
/**
開始了-----1571144894551
Thread-3計算結果:1000000
Thread-0計算結果:1000000
Thread-1計算結果:1000000
Thread-2計算結果:1000000
Thread-4計算結果:1000000
結束了-----1571144894559
總共用時:8
*/
二、CyclicBarrier
【1】CyclicBarrier是什么?
CyclicBarrier即柵欄類,與CountDownLatch類似。它能阻塞一組線程直到某個事件的發(fā)生。柵欄與閉鎖的關鍵區(qū)別在于,所有的線程必須同時到達柵欄位置,才能繼續(xù)執(zhí)行。
CyclicBarrier可以使一定數(shù)量的線程反復地在柵欄位置處匯集。當線程到達柵欄位置時將調用await方法,這個方法將阻塞直到所有線程都到達柵欄位置。如果所有線程都到達柵欄位置,那么柵欄將打開,此時所有的線程都將被釋放,而柵欄將被重置以便下次使用。
【2】CyclicBarrier構造方法
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默認的構造方法是CyclicBarrier(int parties),其參數(shù)表示屏障攔截的線程數(shù)量,每個線程使用await()方法告訴CyclicBarrier我已經(jīng)到達了屏障,然后當前線程被阻塞。
CyclicBarrier的另一個構造函數(shù)CyclicBarrier(int parties, Runnable barrierAction),用于線程到達屏障時,優(yōu)先執(zhí)行barrierAction,方便處理更復雜的業(yè)務場景。
【3】CyclicBarrier應用示例
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è)務處理
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í)行結果中也可以看出,所有的工作線程都運行await()方法之后都到達了柵欄位置,然后,3個工作線程才開始執(zhí)行業(yè)務處理。
【4】CyclicBarrier和CountDownLatch的區(qū)別
- CountDownLatch的計數(shù)器只能使用一次,而CyclicBarrier的計數(shù)器可以使用reset()方法重置,可以使用多次,所以CyclicBarrier能夠處理更為復雜的場景;
- CyclicBarrier還提供了一些其他有用的方法,比如getNumberWaiting()方法可以獲得CyclicBarrier阻塞的線程數(shù)量,isBroken()方法用來了解阻塞的線程是否被中斷;
- CountDownLatch允許一個或多個線程等待一組事件的產(chǎn)生,而CyclicBarrier用于等待其他線程運行到柵欄位置。
三、Semaphore
【1】Semaphore是什么?
信號量(Semaphore),又被稱為信號燈,在多線程環(huán)境下用于協(xié)調各個線程, 以保證它們能夠正確、合理的使用公共資源。信號量維護了一個許可集,我們在初始化Semaphore時需要為這個許可集傳入一個數(shù)量值,該數(shù)量值代表同一時間能訪問共享資源的線程數(shù)量。
【2】Semaphore基本用法
線程可以通過acquire()方法獲取到一個許可,然后對共享資源進行操作,注意如果許可集已分配完了,那么線程將進入等待狀態(tài),直到其他線程釋放許可才有機會再獲取許可,線程釋放一個許可通過release()方法完成,"許可"將被歸還給Semaphore。
【3】Semaphore實現(xiàn)互斥鎖
public class TestSemaphore {
//初始化為1,互斥信號量
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)建一個數(shù)量為1的互斥信號量Semaphore,然后并發(fā)執(zhí)行10個線程,在線程中利用Semaphore控制線程的并發(fā)執(zhí)行,因為信號量數(shù)值只有1,因此每次只能一條線程執(zhí)行,其他線程進入等待狀態(tài)。
四、Callable、Future和FutureTask

Future接口,一般都是取回Callable執(zhí)行的狀態(tài)用的。其中的主要方法:
- cancel,取消Callable的執(zhí)行,當Callable還沒有完成時
- get,獲得Callable的返回值
- isCanceled,判斷是否取消了
- isDone,判斷是否完成
以上就是本文的全部內容,希望對大家的學習有所幫助,也希望大家多多支持腳本之家。
相關文章
詳述IntelliJ IDEA提交代碼前的 Code Analysis 機制(小結)
本篇文章主要介紹了詳述IntelliJ IDEA提交代碼前的 Code Analysis 機制(小結),具有一定的參考價值,感興趣的小伙伴們可以參考一下。2017-11-11
JavaWeb之Servlet注冊頁面的實現(xiàn)示例
注冊頁面是很多網(wǎng)站都會是使用的到,本文主要介紹了JavaWeb之Servlet注冊頁面的實現(xiàn)示例,文中通過示例代碼介紹的非常詳細,具有一定的參考價值,感興趣的小伙伴們可以參考一下2022-04-04
SpringBoot集成netty實現(xiàn)websocket通信功能
Netty是一個高性能、異步事件驅動的網(wǎng)絡應用框架,用于快速開發(fā)可維護的高性能協(xié)議服務器和客戶端,WebSocket 是一種網(wǎng)絡通信協(xié)議,相比傳統(tǒng)的HTTP協(xié)議,本文給大家介紹了SpringBoot集成netty實現(xiàn)websocket通信功能,需要的朋友可以參考下2024-03-03
SpringBoot使用Caffeine實現(xiàn)緩存的示例代碼
本文主要介紹了SpringBoot使用Caffeine實現(xiàn)緩存的示例代碼,文中通過示例代碼介紹的非常詳細,對大家的學習或者工作具有一定的參考學習價值,需要的朋友們下面隨著小編來一起學習學習吧2022-07-07

