日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

Java并发利器:CountDownLatch深度解析与实战应用

發布時間:2025/6/17 编程问答 22 如意码农
生活随笔 收集整理的這篇文章主要介紹了 Java并发利器:CountDownLatch深度解析与实战应用 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

Java并發利器:CountDownLatch深度解析與實戰應用

多線程編程中,讓主線程等待所有子任務完成是個常見需求。CountDownLatch就像一個倒計時器,當所有任務完成后,主線程才繼續執行。本文將通過簡單易懂的方式,帶你掌握這個強大的并發工具。

一、CountDownLatch是什么?

1. 基本概念

CountDownLatch就是一個"倒計數門閂":

  • 倒計數:從指定數字開始遞減到0
  • 門閂:當計數為0時,門閂打開,等待的線程繼續執行
  • 一次性:用完即棄,不能重置
graph TD
A[創建CountDownLatch 3] --> B[啟動3個任務]
B --> C[任務1完成 countDown]
B --> D[任務2完成 countDown]
B --> E[任務3完成 countDown]
C --> F{計數器=0?}
D --> F
E --> F
F -->|是| G[主線程繼續執行]
F -->|否| H[繼續等待]

2. 基本用法

public class CountDownLatchDemo {
public static void main(String[] args) throws InterruptedException {
// 創建計數器,初始值為3
CountDownLatch latch = new CountDownLatch(3); // 啟動3個任務
for (int i = 0; i < 3; i++) {
final int taskId = i;
new Thread(() -> {
System.out.println("任務" + taskId + "開始執行");
try {
Thread.sleep(2000); // 模擬任務執行
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("任務" + taskId + "執行完成");
latch.countDown(); // 計數器減1
}).start();
} System.out.println("主線程等待所有任務完成...");
latch.await(); // 等待計數器變為0
System.out.println("所有任務完成,主線程繼續執行");
}
}

運行結果:

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

二、核心API介紹

CountDownLatch只有4個關鍵方法:

public class CountDownLatchAPI {
public void demonstrateAPI() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3); // 1. countDown() - 計數器減1
latch.countDown(); // 2. await() - 等待計數器變為0
latch.await(); // 3. await(時間, 單位) - 超時等待
boolean finished = latch.await(5, TimeUnit.SECONDS); // 4. getCount() - 獲取當前計數值
long count = latch.getCount();
System.out.println("剩余計數: " + count);
}
}

三、經典應用場景

場景1:等待多個任務完成

最常用的場景,主線程等待所有子任務完成:

public class WaitMultipleTasksDemo {

    // 模擬訂單處理:需要等待庫存檢查、用戶驗證、支付驗證都完成
public void processOrder(String orderId) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(3); // 庫存檢查
new Thread(() -> {
try {
System.out.println("開始庫存檢查...");
Thread.sleep(1000);
System.out.println("庫存檢查完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}).start(); // 用戶驗證
new Thread(() -> {
try {
System.out.println("開始用戶驗證...");
Thread.sleep(1500);
System.out.println("用戶驗證完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}).start(); // 支付驗證
new Thread(() -> {
try {
System.out.println("開始支付驗證...");
Thread.sleep(800);
System.out.println("支付驗證完成");
} catch (InterruptedException e) {
e.printStackTrace();
} finally {
latch.countDown();
}
}).start(); System.out.println("等待所有驗證完成...");
latch.await();
System.out.println("訂單處理完成: " + orderId);
}
}

場景2:控制并發啟動

讓多個線程同時開始執行:

public class ConcurrentStartDemo {

    // 模擬賽跑:所有選手同時起跑
public void startRace() throws InterruptedException {
int runnerCount = 5;
CountDownLatch startGun = new CountDownLatch(1); // 發令槍
CountDownLatch finish = new CountDownLatch(runnerCount); // 終點線 // 創建選手
for (int i = 0; i < runnerCount; i++) {
final int runnerId = i;
new Thread(() -> {
try {
System.out.println("選手" + runnerId + "準備就緒");
startGun.await(); // 等待發令槍 // 開始跑步
System.out.println("選手" + runnerId + "開始跑步");
Thread.sleep(new Random().nextInt(3000)); // 模擬跑步時間
System.out.println("選手" + runnerId + "到達終點"); } catch (InterruptedException e) {
e.printStackTrace();
} finally {
finish.countDown();
}
}).start();
} Thread.sleep(2000); // 等待選手準備
System.out.println("預備...開始!");
startGun.countDown(); // 發令 finish.await(); // 等待所有選手完成
System.out.println("比賽結束!");
}
}

場景3:分段計算

將大任務拆分成小任務并行計算:

public class ParallelCalculationDemo {

    // 并行計算數組的和
public long calculateSum(int[] array) throws InterruptedException {
int threadCount = 4;
CountDownLatch latch = new CountDownLatch(threadCount);
AtomicLong totalSum = new AtomicLong(0); int chunkSize = array.length / threadCount; for (int i = 0; i < threadCount; i++) {
final int start = i * chunkSize;
final int end = (i == threadCount - 1) ? array.length : (i + 1) * chunkSize; new Thread(() -> {
long partialSum = 0;
for (int j = start; j < end; j++) {
partialSum += array[j];
}
totalSum.addAndGet(partialSum);
System.out.println("線程計算范圍[" + start + "," + end + "),結果:" + partialSum);
latch.countDown();
}).start();
} latch.await();
return totalSum.get();
} public static void main(String[] args) throws InterruptedException {
int[] array = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10};
ParallelCalculationDemo demo = new ParallelCalculationDemo();
long result = demo.calculateSum(array);
System.out.println("總和:" + result);
}
}

四、使用注意事項

1. 異常處理要點

核心原則:無論是否異常,都要調用countDown()

//  正確寫法
new Thread(() -> {
try {
// 業務邏輯
doSomething();
} catch (Exception e) {
System.err.println("任務異常:" + e.getMessage());
} finally {
latch.countDown(); // 確保在finally中調用
}
}).start(); // 錯誤寫法
new Thread(() -> {
try {
doSomething();
latch.countDown(); // 異常時不會執行,導致死鎖
} catch (Exception e) {
System.err.println("任務異常:" + e.getMessage());
// 忘記調用countDown()
}
}).start();

2. 避免無限等待

// 設置超時時間,避免無限等待
boolean finished = latch.await(10, TimeUnit.SECONDS);
if (finished) {
System.out.println("所有任務完成");
} else {
System.out.println("等待超時,可能有任務失敗");
}

3. 合理使用線程池

public void useWithThreadPool() throws InterruptedException {
CountDownLatch latch = new CountDownLatch(5);
ExecutorService executor = Executors.newFixedThreadPool(3); for (int i = 0; i < 5; i++) {
final int taskId = i;
executor.submit(() -> {
try {
System.out.println("執行任務" + taskId);
Thread.sleep(1000);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
latch.countDown();
}
});
} latch.await();
executor.shutdown(); // 關閉線程池
System.out.println("所有任務完成");
}

五、實際項目案例

案例:系統啟動初始化

public class SystemInitializer {

    public boolean initializeSystem() {
System.out.println("開始系統初始化..."); CountDownLatch latch = new CountDownLatch(4);
AtomicBoolean success = new AtomicBoolean(true); // 數據庫初始化
new Thread(() -> {
try {
System.out.println("初始化數據庫連接...");
Thread.sleep(2000);
System.out.println("數據庫初始化完成");
} catch (InterruptedException e) {
success.set(false);
} finally {
latch.countDown();
}
}).start(); // Redis初始化
new Thread(() -> {
try {
System.out.println("初始化Redis連接...");
Thread.sleep(1000);
System.out.println("Redis初始化完成");
} catch (InterruptedException e) {
success.set(false);
} finally {
latch.countDown();
}
}).start(); // 配置加載
new Thread(() -> {
try {
System.out.println("加載系統配置...");
Thread.sleep(800);
System.out.println("配置加載完成");
} catch (InterruptedException e) {
success.set(false);
} finally {
latch.countDown();
}
}).start(); // 服務注冊
new Thread(() -> {
try {
System.out.println("注冊服務...");
Thread.sleep(1500);
System.out.println("服務注冊完成");
} catch (InterruptedException e) {
success.set(false);
} finally {
latch.countDown();
}
}).start(); try {
boolean finished = latch.await(10, TimeUnit.SECONDS);
if (finished && success.get()) {
System.out.println("系統初始化成功!");
return true;
} else {
System.out.println("系統初始化失敗!");
return false;
}
} catch (InterruptedException e) {
System.out.println("初始化被中斷");
return false;
}
} public static void main(String[] args) {
SystemInitializer initializer = new SystemInitializer();
initializer.initializeSystem();
}
}

六、總結

CountDownLatch是Java并發編程中的實用工具,它的核心價值在于:

核心特點

  • 簡單易用:API簡潔,概念清晰
  • 線程安全:內部實現保證多線程安全
  • 靈活應用:適合多種并發協作場景

使用要點

  1. 異常安全:在finally中調用countDown()
  2. 超時控制:使用帶超時的await()方法
  3. 一次性使用:CountDownLatch不能重置
  4. 合理設計:根據實際任務數量設置計數器

適用場景

  • 主線程等待多個子任務完成
  • 控制多個線程同時開始執行
  • 分段并行計算后匯總結果
  • 系統啟動時的組件初始化

掌握CountDownLatch,讓你的多線程程序更加優雅和高效!


覺得文章有用?歡迎關注我的微信公眾號【一只劃水的程序猿】,持續分享Java并發編程、性能優化等技術干貨,一起在技術路上精進成長!

總結

以上是生活随笔為你收集整理的Java并发利器:CountDownLatch深度解析与实战应用的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。