JAVA併發之Semaphore(訊號量)

前面幾篇文章我們講了可重入鎖和讀寫鎖(見文末連結),本篇文章主要講下Java併發包下面另一個工具類Semaphore(訊號量)的原理。

Semaphore特點

是一種共享鎖(類似於讀寫鎖中的讀鎖)

基於AQS實現(AQS相關內容可以參考文末連結)

允許一定數量的執行緒獲得鎖

Semaphore例子

JDK原始碼對Semaphore的介紹是可以用來限制一定數量的執行緒訪問某個資源,下面我們透過一個例子來看下具體是什麼意思。

public static void main(String[] args) { DateFormat df = new SimpleDateFormat(“HH:mm:ss——-”); Semaphore semaphore = new Semaphore(3); Runnable runnable = new Runnable() { @Override public void run() { try { semaphore。acquire(); System。out。println(df。format(new Date()) + Thread。currentThread()。getName() + “ got resource”); Thread。sleep(2000); } catch (Exception e) { e。printStackTrace(); } finally { semaphore。release(); } } }; for (int i = 0; i < 9; i++) { new Thread(runnable, “Thread” + i)。start(); }}

上例中,我們建立了一個限制是3的訊號量,同時啟動了9個執行緒去執行各自的任務(這裡都休眠了2秒鐘),從下面列印的結果我們可以看到,同時只有3個執行緒能夠獲得訊號量並執行。

11:12:29——-Thread1 got resource11:12:29——-Thread3 got resource11:12:29——-Thread0 got resource11:12:31——-Thread4 got resource11:12:31——-Thread2 got resource11:12:31——-Thread6 got resource11:12:33——-Thread5 got resource11:12:33——-Thread7 got resource11:12:33——-Thread8 got resource

Semaphore原理

Semaphore內部包含了一個Sync物件繼承了AQS,而Sync物件又有兩個子類NonfaireSync和FairSync,因此Semaphore也支援公平鎖和非公平鎖兩個功能。

JAVA併發之Semaphore(訊號量)

同時,公平鎖和非公平鎖也體現到了Semaphore的建構函式上面:

//預設非公平鎖public Semaphore(int permits) { sync = new NonfairSync(permits);}//fair為true時表示公平鎖public Semaphore(int permits, boolean fair) { sync = fair ? new FairSync(permits) : new NonfairSync(permits);}

獲取Semaphore鎖的方法如下,其主要方法有兩個:

public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread。interrupted()) throw new InterruptedException(); if (tryAcquireShared(arg) < 0) doAcquireSharedInterruptibly(arg);}

tryAcquireShared:

嘗試去獲取鎖,獲取成功則返回正數或0,獲取失敗返回負數。

這裡公平鎖和非公平鎖有不同的實現邏輯:

公平鎖:前執行緒檢查如果AQS裡面有執行緒在排隊了,則當返回-1,進入AQS排隊;否則和其他執行緒競爭獲取鎖,競爭成功則獲取鎖返回,競爭失敗則進入AQS排隊。

protected int tryAcquireShared(int acquires) { for (;;) { if (hasQueuedPredecessors()) return -1; // 透過state來做執行緒數量限制 // state的值和構造函數里面的permits引數是一個值 int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; }}

非公平鎖:當前執行緒直接參與獲取鎖的競爭,競爭成功則獲取鎖返回,競爭失敗則進入AQS排隊(不考慮是否AQS已經有其他執行緒在排隊)。

final int nonfairTryAcquireShared(int acquires) { for (;;) { int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; }}

doAcquireSharedInterruptibly:

類似於讀鎖呼叫的doAcquireShared方法,如果當前執行緒獲取到鎖,則喚醒它在AQS的後繼結點,否則進入AQS排隊。

釋放Semaphore鎖比較簡單,公平鎖和非公平鎖都是一樣的邏輯:

將state數值加1,並且喚醒正在AQS排隊的其他執行緒。

Demo程式碼位置

src/main/java/net/weichitech/juc/SemaphoreTest。java · 小西學程式設計/java-learning - Gitee。com

相關文章

JAVA併發之ReentrantLock原理解析

JAVA併發之ReentrantReadWriteLock原理解析(一)

JAVA併發之ReentrantReadWriteLock原理解析(二)