Java中的CyclicBarrier、CountDownLatch和Semaphore的具體使用
1.CountDownLatch
1.1 介紹和用途
CountDownLatch
是一個同步助手類,在完成一組正在其他線程中執(zhí)行的操作之前,它允許一個或多個線程一直等待。
1.2 工作原理
它通過一個計數(shù)器來實現(xiàn),我們初始化 CountDownLatch
對象時指定計數(shù)器的值,每當一個指定的操作執(zhí)行完成后,計數(shù)值就減一。當計數(shù)值達到零時,它表示所有需要等待的操作都完成了,此時阻塞在 CountDownLatch
上的線程就可以恢復執(zhí)行任務。
1.3 使用場景和示例代碼
CountDownLatch 經(jīng)常用于確保某些操作在繼續(xù)執(zhí)行應用程序剩余部分之前完成,例如,服務器的服務依賴在接受請求前必須初始化完成。
import java.util.concurrent.CountDownLatch; public class ServiceLoader { // 初始計數(shù)器為3,表示需要等待3個服務加載 private static final CountDownLatch latch = new CountDownLatch(3); public static void main(String[] args) throws InterruptedException { // 啟動服務加載線程 new Thread(new Service("Service 1", latch)).start(); new Thread(new Service("Service 2", latch)).start(); new Thread(new Service("Service 3", latch)).start(); // 主線程等待服務加載完成 latch.await(); System.out.println("所有服務已加載完成,服務可以開始接收請求..."); } static class Service implements Runnable { private final String name; private final CountDownLatch latch; public Service(String name, CountDownLatch latch) { this.name = name; this.latch = latch; } @Override public void run() { try { // 模擬服務加載耗時操作 Thread.sleep((long) (Math.random() * 1000)); System.out.println(name + " 服務加載完成."); // 服務加載完成后,計數(shù)器減一 latch.countDown(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }
1.4 注意事項和最佳實踐
CountDownLatch 的計數(shù)器無法被重置,如果需要一個能夠重置計數(shù)的版本,可以考慮使用 CyclicBarrier
。在使用時還需注意異常處理,避免由于異常造成的線程永遠等待的情況。
2.CyclicBarrier
2.1 介紹和用途
CyclicBarrier
是一個同步助手類,它允許一組線程互相等待,直到所有線程都到達一個公共的屏障點(Barrier Point)后才繼續(xù)執(zhí)行。
2.2 工作原理
CyclicBarrier
通過內(nèi)部計數(shù)器來跟蹤到達屏障點的線程數(shù)。當線程到達屏障點時,它調(diào)用 await()
方法,并阻塞直到指定數(shù)量的線程都到達了屏障點。此時,屏障打開,所有阻塞的線程將繼續(xù)執(zhí)行。不同于 CountDownLatch
,CyclicBarrier
是可重用的。
2.3 使用場景和示例代碼
CyclicBarrier
常用于多線程計算數(shù)據(jù)的場景,需要等到全部線程完成計算,才能進行下一步的處理。
import java.util.concurrent.CyclicBarrier; import java.util.concurrent.BrokenBarrierException; public class DataProcessor { private static final int NUMBER_OF_THREADS = 3; private static CyclicBarrier barrier = new CyclicBarrier(NUMBER_OF_THREADS, new Runnable() { @Override public void run() { // 當所有線程到達屏障點時執(zhí)行 System.out.println("所有計算完成,進行數(shù)據(jù)合并..."); } }); public static void main(String[] args) { for(int i = 0; i < NUMBER_OF_THREADS; i++) { new Thread(new Worker(i)).start(); } } static class Worker implements Runnable { private int id; public Worker(int id) { this.id = id; } @Override public void run() { try { // 模擬數(shù)據(jù)處理 System.out.println("線程 #" + id + " 正在處理數(shù)據(jù)..."); Thread.sleep((long) (Math.random() * 1000)); System.out.println("線程 #" + id + " 數(shù)據(jù)處理完成,等待其他線程..."); // 等待其他線程都執(zhí)行到這個點 barrier.await(); System.out.println("線程 #" + id + " 繼續(xù)后續(xù)操作..."); } catch (InterruptedException | BrokenBarrierException e) { e.printStackTrace(); } } } }
2.4 與CountDownLatch的比較
與 CountDownLatch 相比,CyclicBarrier 可以在所有等待線程都被釋放后重置計數(shù)器,而 CountDownLatch 不能重置。
2.5 注意事項和最佳實踐
使用 CyclicBarrier
時需要注意,如果任何線程在等待過程中因為中斷或者超時而提前離開屏障點, 或者等待線程的數(shù)目永遠不足以達到屏障點,這將導致所有在屏障點等待的線程拋出 BrokenBarrierException
。因此,在使用時需要妥善處理這些可能的異常場景。
為了避免這種情形,可以在 await
方法中設置一個超時時間,并適當處理 TimeoutException
。同時,可以通過 isBroken
方法檢查屏障點的狀態(tài),以便在必要時對線程進行重新安排或者重置屏障點。
除此之外,設計上建議只在所有參與線程要完成的任務確實需要互相等待時才使用 CyclicBarrier
,在任務獨立的情況下使用 CountDownLatch
會更為合適。
3.Semaphore
3.1 介紹和用途
Semaphore
(信號量)是一種基于計數(shù)的同步機制,它可以用來控制同時訪問特定資源的線程數(shù)量,是實現(xiàn)資源池或者限制容量的一個有力工具。
3.2 工作原理
Semaphore
管理一組許可證(permits),線程可以通過 acquire()
方法獲取許可證,如果 Semaphore
內(nèi)部計數(shù)為零,表示沒有許可證可用,線程將會阻塞直到有許可證被釋放。相反,線程完成任務后,可以通過 release()
方法釋放許可證,并將其返回給信號量。
3.3 使用場景和示例代碼
Semaphore
通常用于對資源池進行控制,比如數(shù)據(jù)庫連接池,限制同時訪問的連接數(shù),或者在限流控制中限制同時執(zhí)行的線程數(shù)量。
import java.util.concurrent.Semaphore; public class ResourcePool { private static final int MAX_AVAILABLE = 5; private final Semaphore available = new Semaphore(MAX_AVAILABLE, true); public Object getItem() throws InterruptedException { available.acquire(); try { // 模擬獲取資源的操作 return getNextAvailableItem(); } finally { // 保證在資源使用后一定會釋放許可證 available.release(); } } // 此處省略了資源管理的其他邏輯... public static void main(String[] args) { final ResourcePool pool = new ResourcePool(); for (int i = 0; i < 10; i++) { new Thread(() -> { try { Object item = pool.getItem(); // 模擬使用資源 System.out.println(Thread.currentThread().getName() + " 獲取了資源"); Thread.sleep((long) (Math.random() * 1000)); // 假設這里是使用資源完成后的操作 } catch (InterruptedException e) { Thread.currentThread().interrupt(); } }).start(); } } // 此處省略了getNextAvailableItem方法的實現(xiàn),它應該負責分配資源 }
在以上代碼示例中,我們設置了最大并發(fā)數(shù),并通過 Semaphore 來限制訪問資源的線程數(shù)。當所有許可證都被占用時,后來的線程將會等待直到有線程釋放許可證。
3.4 和其他同步工具的比較
相比其他同步工具,Semaphore 提供了對資源的并發(fā)訪問控制,而 CountDownLatch 和 CyclicBarrier 更側重于線程的協(xié)調(diào)和等待。
3.5 注意事項和最佳實踐
使用 Semaphore 時,要確保在資源使用后,準確無誤地釋放許可證,否則可能會導致其他線程永久等待。在實際應用中,尤其在復雜的業(yè)務邏輯里,通常建議使用 try-finally` 語句確保許可證的正確釋放。
同時,合理配置許可證的數(shù)量對于系統(tǒng)的穩(wěn)定性和性能至關重要。對于資源競爭激烈的場景,設置過少的許可證可能會導致系統(tǒng)響應時間增長;反之,設置過多的許可證則可能會超出系統(tǒng)資源的實際承載能力,造成資源的浪費或系統(tǒng)崩潰。
總結
到此這篇關于Java中的CyclicBarrier、CountDownLatch和Semaphore的具體使用的文章就介紹到這了,更多相關Java CyclicBarrier CountDownLatch Semaphore內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關文章希望大家以后多多支持腳本之家!
相關文章
解決MyEclipse中的Building workspace問題的三個方法
這篇文章主要介紹了解決MyEclipse中的Building workspace問題的三個方法,需要的朋友可以參考下2015-11-11Java Servlet簡單實例分享(文件上傳下載demo)
下面小編就為大家?guī)硪黄狫ava Servlet簡單實例分享(文件上傳下載demo)。小編覺得挺不錯的,現(xiàn)在就分享給大家,也給大家做個參考。一起跟隨小編過來看看吧2017-05-05Zookeeper如何實現(xiàn)分布式服務配置中心詳解
Zookeeper在實際使用場景很多,比如配置中心,分布式鎖,注冊中心等,下面這篇文章主要給大家介紹了關于Zookeeper如何實現(xiàn)分布式服務配置中心的相關資料,需要的朋友可以參考下2021-11-11mybatis中實現(xiàn)讓返回值與bean中字段相匹配
這篇文章主要介紹了mybatis中實現(xiàn)讓返回值與bean中字段相匹配,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧2020-10-10