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