併發容器J.U.C -- AQS同步元件(二)
CountDownLatch、Semaphore、CyclicBarrier、ReentrantLock、Condition、FutureTask
CountDownLatch
**計數器向下減的閉鎖 **
同步阻塞類,完成阻塞當前執行緒的功能,給定了一個計數器,原子操作,計數器不能重置。
1.通過一個計數來保證執行緒是否需要被阻塞。實現一個或多個執行緒等待其他執行緒執行的場景。
2.程式需要等待某個條件完成後,才能進行後面的操作(如父任務等待所有子任務都完成的時候,再繼續往下進行)。
我們定義一個CountDownLatch,通過給定的計數器為其初始化,該計數器是原子性操作,保證同時只有一個執行緒去操作該計數器。呼叫該類await方法的執行緒會一直處於阻塞狀態。其他執行緒呼叫countDown方法(每次使計數器-1),當計數器變為0的時候,所有等待的執行緒才會繼續執行。
final CountDownLatch countDownLatch = new CountDownLatch(threadCount);
for (int i = 0; i < threadCount; i++) {
final int threadNum = i;
exec.execute(() -> {
try {
test(threadNum); //需要被等待的執行緒執行的方法
} catch (Exception e) {
log.error("exception", e);
} finally {
countDownLatch.countDown();
}
});
}
countDownLatch.await();
3.多個執行緒完成一個任務,但是這個任務只想給它一個指定的時間,超過這個時間(計數器還未清零)就不繼續等待了,完成多少算多少。(並不是第一時間毀掉所有執行緒,而是先讓正在執行的執行緒執行完)。countDownLatch.await(等待時間長度,時間單位 );
使用場景
查詢需要等待某個條件完成後才能繼續執行後續操作(Ex:平行計算)拆分任務
Semaphore 訊號量
監控併發數
保證同一時間的請求量(併發訪問控制執行緒的數目),達到上限會阻塞
訊號量在作業系統中是很重要的概念,Java併發庫裡的Semaphore就可以很輕鬆的完成類似作業系統訊號量的控制。Semaphore可以很容易控制系統中某個資源被同時訪問的執行緒個數。
在資料結構中我們學過連結串列,連結串列正常是可以儲存無限個節點的,而Semaphore可以實現有限大小的連結串列。
使用場景
- 僅能提供有限訪問的資源。比如資料庫的最大連線數20,而上層的併發數遠遠大於20,若不做限制,可導致併發異常(無法獲取連線)。當Semaphore設定為1時,和單執行緒很相似。
- 併發很高,想要超過允許的併發數之後就拋棄.
/**
* 1、普通呼叫
*/
try {
semaphore.acquire(); // 獲取一個許可
test();//需要併發控制的內容
semaphore.release(); // 釋放一個許可
} catch (Exception e) {
log.error("exception", e);
}
/**
* 2、acquire(n),release(n)
* 同時獲取多個許可,同時釋放多個許可
*/
try {
semaphore.acquire(2);
test();
semaphore.release(2);
} catch (Exception e) {
log.error("exception", e);
}
//tryAcquire()
//tryAcquire(int permits)//permits嘗試獲取許可的次數
//tryAcquire(long timeout, TimeUnit unit);
//tryAcquire(int permits,long timeout, TimeUnit unit)
/*
* 3、tryAcquire())//嘗試獲取一個許可
* 嘗試獲取許可,獲取不到不執行
*/
try {
if (semaphore.tryAcquire()) {
test(threadNum);
semaphore.release();
}
} catch (Exception e) {
log.error("exception", e);
}
/*
* 4、嘗試獲取許可的時候等待一段時間,獲取不到不執行
* 引數1:等待時間長度 引數2:等待時間單位
*/
try {
if (semaphore.tryAcquire(5000, TimeUnit.MILLISECONDS)) {
test(threadNum);
semaphore.release();
}
} catch (Exception e) {
log.error("exception", e);
}
CyclicBarrier
同步輔助類,執行一組執行緒等待到一個公共的屏障點,實現多個執行緒相互等待,所有執行緒都準備就緒後才繼續執行,通過計數器實現的.
當某個執行緒呼叫了await()後,就會進入awaiting等待狀態,並將計數器-1,直到所有的執行緒呼叫await()使計數器為0,執行緒再同時繼續執行。
由於計數器釋放之後可以重用(reset方法),所以稱之為迴圈屏障。
使用場景
多執行緒計算資料,最後合併計算結果。
如Excel儲存使用者的銀行流水,每頁儲存了一個使用者近一年的每筆銀行流水,現統計使用者的日均銀行流水,多執行緒處理每一頁裡的銀行流水。都執行完以後得到每一頁的日均銀行流水。之後通過CyclicBarrier 的action,利用這些執行緒的計算結果,計算出整個Excel的日均流水。
//公共執行緒迴圈呼叫方法
private static CyclicBarrier barrier = new CyclicBarrier(5);
public static void main(String[] args) throws Exception {
ExecutorService executor = Executors.newCachedThreadPool();
for (int i = 0; i < 10; i++) {
final int threadNum = i;
Thread.sleep(1000);
executor.execute(() -> {
try {
race(threadNum);
} catch (Exception e) {
log.error("exception", e);
}
});
}
executor.shutdown();
}
//使用方法1:每個執行緒都持續等待
private static void race(int threadNum) throws Exception {
Thread.sleep(1000);
log.info("{} is ready", threadNum);
barrier.await();
log.info("{} continue", threadNum);
}
//使用方法2:每個執行緒只等待一段時間
private static void race(int threadNum) throws Exception {
Thread.sleep(1000);
try {
barrier.await(2000, TimeUnit.MILLISECONDS);
} catch (InterruptedException | BrokenBarrierException | TimeoutException e) {
log.warn("BarrierException", e);
}
}
//使用方法3:在初始化的時候設定runnable,當執行緒達到屏障時優先執行runnable
private static CyclicBarrier barrier = new CyclicBarrier(5, () -> {
log.info("callback is running");
});
CyclicBarrier與CountDownLatch的比較
CyclicBarrier | CountDownLatch |
---|---|
可重複用 reset() | 只能使用一次 |
多個執行緒相互等待(內部關係) | 一個或n個執行緒等待其他執行緒的關係 |
CyclicBarrier提供方法獲取阻塞執行緒的個數,知道阻塞的執行緒是否中斷
相關文章
- 併發容器J.U.C -- AQS元件(一)AQS元件
- Java併發程式設計,深度探索J.U.C - AQSJava程式設計AQS
- JDK併發AQS系列(二)JDKAQS
- J.U.C - AQSAQS
- Java併發——AbstractQueuedSynchronizer(AQS)同步器JavaAQS
- Java同步容器和併發容器Java
- Java併發(9)- 從同步容器到併發容器Java
- 併發容器與框架——併發容器(二)框架
- 執行緒(十八)J.U.C之AQS:CLH同步佇列執行緒AQS佇列
- Java併發之AQS同步器學習JavaAQS
- 併發-AQSAQS
- Java併發之AQS原始碼分析(二)JavaAQS原始碼
- java併發程式設計:同步容器Java程式設計
- 併發程式設計(一)——同步類容器程式設計
- 併發-7-同步容器和ConcurrentHashMapHashMap
- 詳解AQS的7個同步元件AQS元件
- Java 併發程式設計 —– AQS(抽象佇列同步器)Java程式設計AQS抽象佇列
- Java 併發程式設計 ----- AQS(抽象佇列同步器)Java程式設計AQS抽象佇列
- 併發程式設計(二)——併發類容器ConcurrentMap程式設計
- Java常用併發容器總結(二)Java
- java併發神器 AQS(AbstractQueuedSynchronizer)JavaAQS
- JDK併發AQS系列(一)JDKAQS
- JDK併發AQS系列(三)JDKAQS
- JDK併發AQS系列(五)JDKAQS
- 併發工具類(二)同步屏障CyclicBarrier
- Java併發程式設計序列之JUC底層AQS(二)Java程式設計AQS
- Java併發之AQS原理剖析JavaAQS
- Java併發之AQS詳解JavaAQS
- 同步容器、併發容器、阻塞佇列、雙端佇列與工作密取佇列
- Java併發(5)- ReentrantLock與AQSJavaReentrantLockAQS
- Java併發(6)- CountDownLatch、Semaphore與AQSJavaCountDownLatchAQS
- 併發Lock之AQS(AbstractQueuedSynchronizer)詳解AQS
- [Java併發]AQS的可重入性JavaAQS
- Java併發容器Java
- 高併發程式設計-AQS深入解析程式設計AQS
- [Java原始碼][併發J.U.C]---解析ReentrantReadJava原始碼
- 併發容器之CopyOnWriteArrayList
- 併發程式設計——詳解 AQS CLH 鎖程式設計AQS