CountDownLatch 詳細介紹

CountDownLatch 是 Java 中 java.util.concurrent 包提供的一個同步工具類,用于協調多個線程之間的執行順序。它允許一個或多個線程等待,直到其他線程完成一組操作后繼續執行。CountDownLatch 是一種倒計數鎖存器,通過設置一個初始計數器值,線程可以通過調用 countDown() 方法遞減計數器,當計數器達到 0 時,等待的線程會被喚醒。

主要特點
  1. 一次性使用CountDownLatch 是一次性工具,一旦計數器減到 0,它不能被重置或重用。如果需要可重置的機制,可以考慮使用 CyclicBarrier
  2. 線程等待:一個或多個線程可以通過 await() 方法等待計數器歸零。
  3. 計數器遞減:通過 countDown() 方法,線程可以減少計數器的值。
  4. 線程安全CountDownLatch 是線程安全的,適合多線程并發場景。
  5. 靈活性:可以用于等待一組任務完成后再執行后續邏輯,或者讓主線程等待多個子線程完成。
工作原理
  • CountDownLatch 內部維護一個計數器,初始化時指定一個正整數值。
  • 調用 countDown() 方法會將計數器減 1,直到計數器為 0。
  • 調用 await() 方法的線程會阻塞,直到計數器為 0,所有等待的線程會被喚醒。
  • 使用基于 AQS(AbstractQueuedSynchronizer)的同步機制,確保線程安全和高效的等待/通知機制。
常見方法
  • CountDownLatch(int count):構造方法,初始化計數器為指定的值 count
  • void countDown():將計數器減 1,如果計數器達到 0,喚醒所有等待的線程。
  • void await():使當前線程阻塞,直到計數器歸零。
  • boolean await(long timeout, TimeUnit unit):使當前線程阻塞,直到計數器歸零或超時,返回是否成功(true 表示計數器歸零,false 表示超時)。
  • long getCount():返回當前計數器的值。

使用場景

  1. 等待多個線程完成:主線程等待多個子線程完成任務后再繼續執行。
  2. 并行任務協調:在任務分解為多個子任務時,確保所有子任務完成后再進行匯總。
  3. 啟動信號:確保多個線程同時開始執行(通過設置計數器為 1,主線程調用 countDown() 觸發所有線程)。
  4. 測試并發場景:在性能測試中,模擬多個線程同時執行某個任務。

CountDownLatch 示例代碼

以下是幾個使用 CountDownLatch 的實際例子,展示其在不同場景下的應用。

示例 1:主線程等待多個子線程完成
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class CountDownLatchExample1 {public static void main(String[] args) throws InterruptedException {int threadCount = 3;// 創建一個計數器,初始值為3CountDownLatch latch = new CountDownLatch(threadCount);// 創建線程池ExecutorService executor = Executors.newFixedThreadPool(threadCount);// 提交三個任務for (int i = 1; i <= threadCount; i++) {final int taskId = i;executor.submit(() -> {try {System.out.println("任務 " + taskId + " 開始執行");Thread.sleep((int) (Math.random() * 1000)); // 模擬任務耗時System.out.println("任務 " + taskId + " 完成");latch.countDown(); // 計數器減1} catch (InterruptedException e) {e.printStackTrace();}});}// 主線程等待所有任務完成System.out.println("主線程等待所有任務完成...");latch.await();System.out.println("所有任務已完成,主線程繼續執行");// 關閉線程池executor.shutdown();}
}

輸出示例

主線程等待所有任務完成...
任務 1 開始執行
任務 2 開始執行
任務 3 開始執行
任務 1 完成
任務 3 完成
任務 2 完成
所有任務已完成,主線程繼續執行

解釋

  • 創建一個 CountDownLatch,初始計數器為 3。
  • 三個線程分別執行任務,并在完成后調用 countDown() 減少計數器。
  • 主線程調用 await(),阻塞直到計數器為 0。
  • 當所有線程完成任務后,計數器歸零,主線程繼續執行。
示例 2:模擬并發任務啟動

這個例子展示如何使用 CountDownLatch 讓多個線程同時開始執行任務。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;public class CountDownLatchExample2 {public static void main(String[] args) throws InterruptedException {int threadCount = 5;// 創建一個計數器,初始值為1CountDownLatch startSignal = new CountDownLatch(1);// 創建線程池ExecutorService executor = Executors.newFixedThreadPool(threadCount);// 提交5個任務,等待啟動信號for (int i = 1; i <= threadCount; i++) {final int workerId = i;executor.submit(() -> {try {System.out.println("工人 " + workerId + " 準備就緒");startSignal.await(); // 等待啟動信號System.out.println("工人 " + workerId + " 開始工作");Thread.sleep((int) (Math.random() * 1000)); // 模擬工作System.out.println("工人 " + workerId + " 完成工作");} catch (InterruptedException e) {e.printStackTrace();}});}// 主線程模擬準備時間Thread.sleep(1000);System.out.println("所有工人準備就緒,主線程發出啟動信號!");startSignal.countDown(); // 發出啟動信號// 關閉線程池executor.shutdown();}
}

輸出示例

工人 1 準備就緒
工人 2 準備就緒
工人 3 準備就緒
工人 4 準備就緒
工人 5 準備就緒
所有工人準備就緒,主線程發出啟動信號!
工人 1 開始工作
工人 2 開始工作
工人 3 開始工作
工人 4 開始工作
工人 5 開始工作
工人 3 完成工作
工人 1 完成工作
工人 5 完成工作
工人 2 完成工作
工人 4 完成工作

解釋

  • 使用 CountDownLatch 的計數器為 1,模擬一個啟動信號。
  • 所有工人線程調用 await() 等待主線程的信號。
  • 主線程調用 countDown() 后,所有工人線程幾乎同時開始工作。
示例 3:帶超時的等待

這個例子展示如何使用帶超時的 await 方法。

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;public class CountDownLatchExample3 {public static void main(String[] args) throws InterruptedException {int threadCount = 2;CountDownLatch latch = new CountDownLatch(threadCount);ExecutorService executor = Executors.newFixedThreadPool(threadCount);// 提交兩個任務executor.submit(() -> {try {System.out.println("任務 1 開始執行");Thread.sleep(1000); // 模擬耗時任務System.out.println("任務 1 完成");latch.countDown();} catch (InterruptedException e) {e.printStackTrace();}});executor.submit(() -> {try {System.out.println("任務 2 開始執行");Thread.sleep(3000); // 模擬更長的耗時任務System.out.println("任務 2 完成");latch.countDown();} catch (InterruptedException e) {e.printStackTrace();}});// 主線程等待,最多等待2秒System.out.println("主線程等待,最多2秒...");boolean completed = latch.await(2, TimeUnit.SECONDS);if (completed) {System.out.println("所有任務在2秒內完成");} else {System.out.println("超時,部分任務未完成");}executor.shutdown();}
}

輸出示例

主線程等待,最多2秒...
任務 1 開始執行
任務 2 開始執行
任務 1 完成
超時,部分任務未完成
任務 2 完成

解釋

  • 主線程設置了 2 秒的超時時間,通過 await(2, TimeUnit.SECONDS)
  • 任務 1 在 1 秒內完成,但任務 2 需要 3 秒,因此主線程在超時后繼續執行,輸出“部分任務未完成”。

CountDownLatch 與其他工具的對比

  1. 與 CyclicBarrier 的區別
    • CountDownLatch 是一次性的,計數器歸零后無法重用;CyclicBarrier 可以重置并重復使用。
    • CountDownLatch 通常用于主線程等待子線程;CyclicBarrier 更適合多個線程相互等待。
  2. 與 Semaphore 的區別
    • Semaphore 用于控制資源訪問的并發數;CountDownLatch 用于協調線程的完成順序。
  3. 與 wait/notify
    • CountDownLatch 提供了更簡單、更高級的同步機制,基于 AQS 實現,性能更優。

使用建議

  • 適用場景:當需要等待一組任務完成后再執行后續邏輯,或需要多個線程同時開始時。
  • 不適用場景:如果需要重復使用計數器,建議使用 CyclicBarrier;如果需要控制并發訪問量,建議使用 Semaphore
  • 注意事項
    • 確保初始計數器值合理,過高的計數可能導致線程長時間等待。
    • countDown() 調用過多不會導致計數器變成負數,但應避免邏輯錯誤。
    • 使用帶超時的 await 方法可以避免無限等待。

總結

CountDownLatch 是一個簡單而強大的同步工具,適用于協調多線程任務的完成順序或統一啟動。通過設置計數器和使用 await/countDown 方法,它能有效地控制線程間的協作。上述示例展示了其在等待任務完成、統一啟動并發任務和帶超時等待的場景中的應用。

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/web/87612.shtml
繁體地址,請注明出處:http://hk.pswp.cn/web/87612.shtml
英文地址,請注明出處:http://en.pswp.cn/web/87612.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Hadoop之HDFS

Hadoop之HDFS HDFS的Shell操作 啟動Hadoop集群(方便后續測試) [atguigu@hadoop102 ~]$ sbin/start-dfs.sh [atguigu@hadoop102 ~]$ sbin/start-yarn.sh-help:輸出這個命令參數 [atguigu@hadoop102 ~]$ hadoop fs -help rm-ls:顯示目錄信息 [atguigu@hadoop102 ~]$ hadoop …

【1.4 漫畫PostgreSQL高級數據庫及國產數據庫對比】

&#x1f418; 漫畫PostgreSQL高級數據庫及國產數據庫對比 &#x1f468;?&#x1f4bb; 小明&#xff1a;“老王&#xff0c;除了MySQL&#xff0c;還有哪些優秀的關系型數據庫&#xff1f;國產數據庫發展得怎么樣&#xff1f;” &#x1f9d9;?♂? 架構師老王&#xff1a;…

OLT、ONU、ONT、SFU、HGU、ODN,它們是什么?它們之間有什么區別?

我們經常會看到OLT、ONU、ONT、SFU、HGU等設備術語。它們分別是什么?又有什么區別呢? PON組件:OLT、ONU、ONT和ODN 無源光網絡(PON)采用光纖和分路器&#xff0c;以點對多點拓撲將數據從單一源分發到多個用戶。與有源光網絡 (AON)不同&#xff0c;PON 僅在光域中運行&#…

sql USING 簡化 JOIN 操作

在 SQL 中&#xff0c;USING 是一種用于簡化 JOIN 操作的語法糖&#xff0c;它允許你明確指定連接表時所依據的列名。與傳統的 ON 子句相比&#xff0c;USING 提供了更簡潔的語法1. 基本語法與作用table1 JOIN table2 USING (column_name);將 table1 和 table2 中 column_name …

android開發中的 AndroidX 版本的查看 及 constraintLayout的簡單用法

1、查看庫的版本 平常我們經常會用到一些庫&#xff0c;但是不知道是什么版本&#xff0c;也不知道最新的是什么版本&#xff0c;當然最好的就是到官網去查看&#xff0c;或者三方的maven庫。 2、官方地址 AndroidX 版本 | Jetpack | Android Developers 3、比如我們來…

oracle鎖表,oracle解鎖表,oracle用戶連接數

一、查看被鎖的表 select sess.sid, sess.serial#, lo.oracle_username, lo.os_user_name, ao.object_name, lo.locked_mode from v$locked_object lo, dba_objects ao, v$session sess where ao.object_id lo.object_id and lo.session_id sess.sid; 二、解鎖表語句 alter …

3D可視化:開啟多維洞察新時代

3D可視化技術以一種前所未有的方式&#xff0c;將數據、模型與現實世界緊密相連&#xff0c;為人們帶來了沉浸式、交互式的全新體驗&#xff0c;徹底革新了信息的呈現與理解方式。一、3D可視化的技術原理從技術本質來看&#xff0c;3D可視化基于一系列復雜而精妙的原理。通過數…

List中的對象進行排序處理

以下是使用 Java Stream 對對象列表按 id 和 age 排序的完整示例&#xff0c;包含升序和降序兩種場景&#xff1a; 1. 定義測試對象類 Getter Setter public class Person {private int id;private int age; }2. 排序實現代碼 import java.util.*; import java.util.stream.…

秋招Day14 - Redis - 底層結構

Redis都有哪些底層數據結構&#xff1f; 有八種核心的底層數據結構。 SDS Redis自己實現的動態字符串&#xff0c;SDS結構中直接存儲了已使用的字符數組長度len和未使用的字符數組長度free&#xff0c;所以獲取長度的時間復雜度是O(1)&#xff0c;還支持動態擴容&#xff0c…

使用Mac自帶的圖像捕捉導出 iPhone 相冊

用 數據線 將 iPhone 連接到 Mac必須是數據線,有些充電線插上去后無法識別到iphone在 iPhone 上點擊“信任此電腦”在 Mac 上打開應用&#xff1a;快速方式&#xff1a;按 Command Space 打開 Spotlight&#xff0c;輸入 圖像捕捉 或 Image Capture&#xff0c;回車或者從 /系…

【UniApp picker-view 多列對齊問題深度剖析與完美解決】

UniApp picker-view 多列對齊問題深度剖析與完美解決一次看似簡單的樣式調整&#xff0c;卻引發了對構建工具、CSS 預處理和組件渲染機制的深度思考創作時間: 2025/7/1 技術棧: UniApp Vue3 TypeScript PostCSS 問題級別: &#x1f534; 高級&#x1f3af; 問題背景 在開發 …

R Studio開發中記錄

1.如何將tar.gz格式的源碼R包編譯為zip格式的二進制R包。 R CMD INSTALL --build knhanes.tar.gz R CMD INSTALL --build nhanes.tar.gz 2.下載RTools RTools: Toolchains for building R and R packages from source on Windows 3.修改環境變量 PATH$PATH:/d/rtools45/usr…

量化交易中的隱藏模式識別:基于潛在高斯混合模型的機會挖掘

*——從市場噪聲中提取黃金信號的數學藝術** > 2025年3月,某對沖基金使用潛在高斯混合模型捕捉到銅期貨的異常波動模式,提前布局實現單月收益47%。核心代碼僅20行,卻顛覆了傳統技術分析范式。 --- ### 01 市場迷思:為何90%的交易者失敗? 金融市場本質是**非…

Qt窗口被外部(非Qt內部機制)強制銷毀,第二次再重復使用不顯示

在Qt開發中&#xff0c;窗口被外部&#xff08;非Qt內部機制&#xff09;強制銷毀 警告信息 External WM_DESTROY received for QWidgetWindow(0x108b8cbdb10, name"xxxxx") , parent: QWindow(0x0) , transient parent: QWindow(0x0) 使用場景 代碼結構如下&#x…

一文詳解Character AI:實用指南+ ChatGPT、Gemini對比分析

本指南將深入剖析Character AI的運行機制、功能特性及其存在的局限性。 近年來&#xff0c;生成式人工智能領域發展態勢迅猛&#xff0c;其應用范疇已遠超單純的文本生成領域。在眾多備受矚目的新興平臺中&#xff0c;Character AI是一款支持用戶以對話形式與人工智能生成角色…

遺傳算法的原理與實現示例

遺傳算法是一種受生物進化理論啟發的隨機優化算法&#xff0c;其核心思想是模擬自然界中 “物競天擇、適者生存” 的進化過程&#xff0c;通過對候選解的迭代優化&#xff0c;找到問題的最優解。 一、核心思想 遺傳算法將優化問題的候選解視為生物群體中的“個體”&#xff0c…

centos7 ping127.0.0.1不通

ping 127.0.0.1&#xff0c;localhost和本地ip都不通&#xff0c;所有的配置也是正確的 檢查下是否禁止了ping vim /proc/sys/net/ipv4/icmp_echo_ignore_all 內容為 1 禁止ping 內容為0 開啟ping sysctl -w net.ipv4.icmp_echo_ignore_all0 變更以上設置即可

【無標題】JavaScript入門

JS 1.JS引入方式 <!DOCTYPE html><html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>JS-引入方式</title><!-- …

(JAVA)自建應用調用企業微信API接口,實現消息推送

建議先簡單了解企業微信開發者中心文檔&#xff1a;開發前必讀 - 文檔 - 企業微信開發者中心 了解一下企業微信調用接口的基礎參數&#xff1a;基本概念介紹 - 文檔 - 企業微信開發者中心 本篇每個步驟都會跟著官網文檔走&#xff0c;都會貼上相關鏈接&#xff0c;看完本篇文…

P/Invoke 在默認封送(marshalling)規則下,常見托管 ? 非托管類型的對應關系

下表整理了 P/Invoke 在默認封送&#xff08;marshalling&#xff09;規則下&#xff0c;常見托管???非托管類型的對應關系。 內容主要依據微軟官方 Marshalling Data with?Platform?Invoke 文檔&#xff0c;并補充了常見指針&#xff0f;句柄用法與字符串緩沖區&#xff…