聊聊高并发(二十六)解析java.util.concurrent各个组件(八) 理解CountDownLatch闭锁
CountDownLatch閉鎖也是基于AQS實現的一種同步器,它表示了“所有線程都等待,直到鎖打開才繼續執行”的含義。它和Semaphore的語意不同, Semaphore的獲取和釋放操作都會修改狀態,都可能讓自己或者其他線程立刻拿到鎖。而閉鎖的獲取操作只判斷狀態是否為0,不修改狀態本身,閉鎖的釋放操作會修改狀態,每次遞減1,直到狀態為0。
所以正常情況下,閉鎖的獲取操作只是等待,不會立刻讓自己獲得鎖,直到釋放操作把狀態變為0。
閉鎖可以用來實現很多場景,比如:
1. 某個服務依賴于其他服務的啟動才能啟動,就可以讓這個服務在其他服務狀態的閉鎖上等待
2. 某個游戲,必須等所有就緒者都到達才能開始游戲
3. 啟動一組相關的線程
4. 等待一組相關線程結束
?
來看看CountDownLatch的代碼。它也提供了一個內部類Sync來繼承AQS
1. CountDownLatch可以讓多個線程同時進入臨界區,所以也是共享模式的AQS
2. 獲取操作只是判斷狀態是否為0,即是否可以結束等待,進入臨界區
3. 釋放操作是對狀態遞減1,所以叫CountDown,類似報數的意思
?
?private static final class Sync extends AbstractQueuedSynchronizer {
private static final long serialVersionUID = 4982264981922014374L;
Sync(int count) {
setState(count);
}
int getCount() {
return getState();
}
protected int tryAcquireShared(int acquires) {
return (getState() == 0) ? 1 : -1;
}
protected boolean tryReleaseShared(int releases) {
// Decrement count; signal when transition to zero
for (;;) {
int c = getState();
if (c == 0)
return false;
int nextc = c-1;
if (compareAndSetState(c, nextc))
return nextc == 0;
}
}
}
?
?
CountDownLatch維護了一個狀態表示Count的總數,釋放一次對這個總數減1直到為0,它的tryXXX方法傳遞的參數沒有實際意義,只是為了適應接口。
如果獲取失敗,就進入AQS等待,直到等待結束后,以共享的方式在AQS隊列中釋放線程。
CountDownLatch常用的方法就兩個: await()和countDown()
?
?public CountDownLatch(int count) {
if (count < 0) throw new IllegalArgumentException("count < 0");
this.sync = new Sync(count);
}
// 等待就相當于獲取操作
public void await() throws InterruptedException {
??????? sync.acquireSharedInterruptibly(1);
??? }
// 限時等待
public boolean await(long timeout, TimeUnit unit)
??????? throws InterruptedException {
??????? return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
??? }
public void countDown() {
??????? sync.releaseShared(1);
??? }
設計1個測試用例來測試CountDownLatch閉鎖的功能
?
1.? 創建1個二元閉鎖startLatch,只有1和0兩種狀態,也就是說只執行一次countDown()就可以打開閉鎖。這個startLatch用來阻塞線程,直到主線程說可以開始了
2.? 創建1個狀態為n的endLatch,線程執行完就調用一次countDown,主線程在endLatch阻塞,直到n個線程都執行了countDown()報數,主線程才打印結束
?
?
?package com.zc.lock;
import java.util.concurrent.CountDownLatch;
public class CountDownLatchUsecase {
private int nThreads;
private CountDownLatch startLatch;
private CountDownLatch endLatch;
public CountDownLatchUsecase(int n){
this.nThreads = n;
startLatch = new CountDownLatch(1);
endLatch = new CountDownLatch(nThreads);
}
public void race() throws InterruptedException{
System.out.println("Thread " + Thread.currentThread().getName() + " is waiting the resource");
startLatch.await();
System.out.println("Thread " + Thread.currentThread().getName() + " got the resource");
endLatch.countDown();
}
public void start(){
startLatch.countDown();
}
public void end() throws InterruptedException{
endLatch.await();
}
public static void main(String[] args) throws Exception{
final CountDownLatchUsecase usecase = new CountDownLatchUsecase(10);
for(int i = 0; i < 10; i++){
Thread t = new Thread(new Runnable(){
@Override
public void run() {
try {
usecase.race();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}, String.valueOf(i));
t.start();
}
Thread.sleep(3000);
System.out.println("Now start!!!");
usecase.start();
usecase.end();
System.out.println("All Thread finished");
}
}
測試結果: 所有線程都等待,直到主線程說開始。所有線程都執行了countDown()之后,主線程才說結束
?
?
?Thread 0 is waiting the resource
Thread 2 is waiting the resource
Thread 3 is waiting the resource
Thread 1 is waiting the resource
Thread 4 is waiting the resource
Thread 5 is waiting the resource
Thread 6 is waiting the resource
Thread 7 is waiting the resource
Thread 8 is waiting the resource
Thread 9 is waiting the resource
Now start!!!
Thread 0 got the resource
Thread 2 got the resource
Thread 3 got the resource
Thread 8 got the resource
Thread 7 got the resource
Thread 6 got the resource
Thread 5 got the resource
Thread 1 got the resource
Thread 4 got the resource
Thread 9 got the resource
All Thread finished
總結
以上是生活随笔為你收集整理的聊聊高并发(二十六)解析java.util.concurrent各个组件(八) 理解CountDownLatch闭锁的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 聊聊高并发(二十五)解析java.uti
- 下一篇: 聊聊高并发(二十七)解析java.uti