首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

JDK5 線程同步工具類深度解析(三):Semaphore、CyclicBarrier 實戰應用

     編輯:C++ 入門

JDK5 線程同步工具類深度解析(三):Semaphore、CyclicBarrier 實戰應用

一、Semaphore 信號量 - 控制並發訪問數量

核心概念

Semaphore 維護一組「許可證」,線程調用 acquire() 獲取許可(無則阻塞),用完調用 release() 歸還。構造函數參數指定許可證總數,可選 fair 參數控制公平性(FIFO vs 非公平)。

經典場景:資源池並發控制

如:數據庫連接池限制 10 並發、文件同時寫入限制 5 個線程、廁所有 5 個坑位同時容納 5 人。

完整代碼示例

import java.util.concurrent.*;

public class SemaphoreTest {
    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();
        final Semaphore sp = new Semaphore(3);  // 允許 3 並發
        
        for (int i = 0; i < 10; i++) {
            es.execute(() -> {
                try {
                    sp.acquire();  // 獲取許可
                    System.out.printf("線程 %s 進入,當前並發: %d%n",
                        Thread.currentThread().getName(), 3 - sp.availablePermits());
                    Thread.sleep(2000);  // 模擬業務
                    System.out.printf("線程 %s 即將離開%n", Thread.currentThread().getName());
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } finally {
                    sp.release();  // 歸還許可
                    System.out.printf("線程 %s 離開,當前並發: %d%n",
                        Thread.currentThread().getName(), 3 - sp.availablePermits());
                }
            });
        }
        es.shutdown();
    }
}

單許可 Semaphore 實現互斥鎖

new Semaphore(1) 等價於互斥鎖,但更靈活:可由線程 A acquire(),線程 B release(),適用於死鎖恢復等特殊場景。

二、CyclicBarrier 循環柵欄 - 多線程同步等待

核心概念

CyclicBarrier 讓一組線程相互等待,直到所有線程都到達屏障點(調用 await()),然後同時繼續執行。可選構造參數 Runnable barrierAction 在所有線程到達後、繼續前由最後一個到達的線程執行。

經典場景:分階段計算、多玩家遊戲同步、MapReduce 階段同步

完整代碼示例

import java.util.concurrent.*;

public class CyclicBarrierTest {
    public static void main(String[] args) {
        ExecutorService service = Executors.newCachedThreadPool();
        final CyclicBarrier cb = new CyclicBarrier(3);  // 3 個線程
        
        for (int i = 0; i < 3; i++) {
            service.execute(() -> {
                try {
                    Thread.sleep((long)(Math.random() * 10000));  // 隨機準備時間
                    System.out.printf("線程 %s 到達集合點1,已有 %d 個到達,%s%n",
                        Thread.currentThread().getName(),
                        cb.getNumberWaiting() + 1,
                        cb.getNumberWaiting() == 2 ? "都到齊了,繼續前進" : "正在等候");
                    cb.await();  // 等待其他線程
                    
                    Thread.sleep((long)(Math.random() * 10000));
                    System.out.printf("線程 %s 到達集合點2,已有 %d 個到達,%s%n",
                        Thread.currentThread().getName(),
                        cb.getNumberWaiting() + 1,
                        cb.getNumberWaiting() == 2 ? "都到齊了,繼續前進" : "正在等候");
                    cb.await();
                    
                    Thread.sleep((long)(Math.random() * 10000));
                    System.out.printf("線程 %s 到達集合點3,已有 %d 個到達,%s%n",
                        Thread.currentThread().getName(),
                        cb.getNumberWaiting() + 1,
                        cb.getNumberWaiting() == 2 ? "都到齊了,繼續前進" : "正在等候");
                    cb.await();
                } catch (Exception e) { e.printStackTrace(); }
            });
        }
        service.shutdown();
    }
}

三、核心區別與選型指南

特性SemaphoreCyclicBarrier
核心目的限制同時訪問資源的線程數讓多線程在屏障點同步會合
同步方向單向(獲取→釋放)雙向(所有線程互相等待)
可重用性永久可用循環可用(await 後自動重置)
典型場景連接池、限流、資源隔離分階段任務、並行計算同步
公平性選項支持公平/非公平無公平性選項

四、進階用法提示

  • Semaphore:tryAcquire(timeout) 超時獲取、availablePermits() 監控剩餘許可、drainPermits() 獲取並清空所有許可
  • CyclicBarrier:getNumberWaiting() 獲取當前等待線程數、isBroken() 檢查是否損壞(線程中斷/超時導致)、reset() 重置屏障
  • 對比 CountDownLatch:CountDownLatch 一次性、不可重置;CyclicBarrier 可循環使用

五、最佳實踐總結

  1. 需要限制並發度時首選 Semaphore
  2. 需要多線程階段同步時首選 CyclicBarrier
  3. 始終在 finally 塊中 release(),防止許可洩漏
  4. 避免在持有許可時執行耗時 I/O 或阻塞操作
  5. 根據業務選擇公平模式:高爭用場景公平模式吞吐略低但避免飢餓
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved