摘要:信號可以理解為一種許可,拿到許可的線程才可以繼續(xù)執(zhí)行。的計數(shù)器其實記錄的就是許可的數(shù)量,當(dāng)許可數(shù)量為時,方法就會阻塞。
本文接著分析Semaphore的實現(xiàn)原理
Semaphore是什么Semaphore是一個計數(shù)信號量。Semaphore(信號)可以理解為一種許可,拿到許可的線程才可以繼續(xù)執(zhí)行。Semaphore的計數(shù)器其實記錄的就是許可的數(shù)量,當(dāng)許可數(shù)量為0時,acquire方法就會阻塞。這個系統(tǒng)和停車位系統(tǒng)非常相似,當(dāng)停車場還有空位的時候,任何新來的車輛都可以進(jìn),當(dāng)停車場滿的時候,新來的車輛必須要等到有空車位產(chǎn)生的時候才可以開進(jìn)停車場。這里的停車位就相當(dāng)于Semaphore的許可數(shù)量。
Semaphore的使用方法public static void main(String[] args) throws InterruptedException{ Semaphore semaphore = new Semaphore(3); for (int i = 1; i <= 5; i++) { final int threadNum = i; new Thread(() -> { try { semaphore.acquire(); System.out.println("thread" + threadNum + ":entered"); Thread.sleep(1000 * threadNum); System.out.println("thread" + threadNum + ":gone"); semaphore.release(); } catch (InterruptedException e) { System.out.println("thread" + threadNum + ":exception"); } }).start(); } }
看一下輸出結(jié)果:
thread2:entered thread3:entered thread1:entered thread1:gone thread4:entered thread2:gone thread5:entered thread3:gone thread4:gone thread5:gone Process finished with exit code 0
首先我們new了一個信號量,給了3個許可,然后在新建5個線程搶占信號量。一開始1,2,3號線程可以拿到許可,然后4號線程來了,發(fā)現(xiàn)沒有許可了,4號線程阻塞,直到1號線程調(diào)用了release后有了許可后,4號線程被喚醒,以此類推。。。
Semaphore原理Semaphore和Reentrant一樣分別實現(xiàn)了公平鎖和非公平鎖,同樣我們看非公平鎖。上Sync內(nèi)部類代碼:
abstract static class Sync extends AbstractQueuedSynchronizer { private static final long serialVersionUID = 1192457210091910933L; //構(gòu)造函數(shù),接收許可的個數(shù) Sync(int permits) { setState(permits); } final int getPermits() { return getState(); } //共享模式下,非公平鎖搶占 final int nonfairTryAcquireShared(int acquires) { for (;;) { int available = getState(); //可用許可數(shù)量減去申請許可數(shù)量 int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) //返回remaining,小于0表示許可數(shù)量不夠,大于0表示許可數(shù)量足夠 return remaining; } } //共享模式下釋放許可 protected final boolean tryReleaseShared(int releases) { for (;;) { int current = getState(); int next = current + releases; if (next < current) // overflow throw new Error("Maximum permit count exceeded"); if (compareAndSetState(current, next)) return true; } } //減少許可數(shù)量 final void reducePermits(int reductions) { for (;;) { int current = getState(); int next = current - reductions; if (next > current) // underflow throw new Error("Permit count underflow"); if (compareAndSetState(current, next)) return; } } //清空許可數(shù)量 final int drainPermits() { for (;;) { int current = getState(); if (current == 0 || compareAndSetState(current, 0)) return current; } } }
了解了Semaphore對state的定義后,我們看一下acquire方法,該方法直接調(diào)用了sync的acquireSharedInterruptibly:
public final void acquireSharedInterruptibly(int arg) throws InterruptedException { //線程中斷標(biāo)志為true,拋出中斷異常 if (Thread.interrupted()) throw new InterruptedException(); //首先嘗試獲取需要的許可數(shù)量 if (tryAcquireShared(arg) < 0) //當(dāng)獲取失敗 doAcquireSharedInterruptibly(arg); }
doAcquireSharedInterruptibly在CountdownLatch里分析過:當(dāng)獲取許可失敗時,往等待隊列添加當(dāng)前線程的node,如果隊列沒有初始化則初始化。然后在一個loop里從隊列head后第一個node開始嘗試獲取許可,為了不讓CPU空轉(zhuǎn),當(dāng)head后第一個node嘗試獲取許可失敗的時候,阻塞當(dāng)前線程,第一個node后的弄的一樣都阻塞,等待被喚醒。
private void doAcquireSharedInterruptibly(int arg) throws InterruptedException { final Node node = addWaiter(Node.SHARED); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); if (p == head) { int r = tryAcquireShared(arg); if (r >= 0) { setHeadAndPropagate(node, r); p.next = null; // help GC failed = false; return; } } if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt()) throw new InterruptedException(); } } finally { if (failed) cancelAcquire(node); } }
當(dāng)調(diào)用了release方法后,意味著有新的許可被釋放,調(diào)用sync的releaseShared,接著調(diào)用Semaphore的內(nèi)部類Sync實現(xiàn)的tryReleaseShared嘗試釋放許可。釋放成功后調(diào)用AQS的doReleaseShared,在CountdownLatch中也見過這個方法。在之前head后第一個node線程阻塞之前,已經(jīng)將head狀態(tài)設(shè)置為SIGNAL,所以會喚醒第一個node的線程,該線程繼續(xù)執(zhí)行之前的loop,嘗試獲取許可成功,并且當(dāng)還有剩余的許可存在時向后傳播喚醒信號,喚醒后繼node的線程,獲取剩余的許可。
總結(jié)Semaphore和CountdownLatch一樣使用了AQS的共享模式;
Semaphore在有許可釋放時喚醒第一個node的線程獲取許可,之后會根據(jù)是否還存在許可來決定是否繼續(xù)往后傳播喚醒線程的信號。
CountdownLatch在state為0的時候依次往后傳播喚醒信號,一直傳播到低,直到所有線程被喚醒。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://specialneedsforspecialkids.com/yun/68455.html
摘要:信號可以理解為一種許可,拿到許可的線程才可以繼續(xù)執(zhí)行。的計數(shù)器其實記錄的就是許可的數(shù)量,當(dāng)許可數(shù)量為時,方法就會阻塞。 本文接著分析Semaphore的實現(xiàn)原理 Semaphore是什么 Semaphore是一個計數(shù)信號量。Semaphore(信號)可以理解為一種許可,拿到許可的線程才可以繼續(xù)執(zhí)行。Semaphore的計數(shù)器其實記錄的就是許可的數(shù)量,當(dāng)許可數(shù)量為0時,acquire方法...
摘要:信號可以理解為一種許可,拿到許可的線程才可以繼續(xù)執(zhí)行。的計數(shù)器其實記錄的就是許可的數(shù)量,當(dāng)許可數(shù)量為時,方法就會阻塞。 本文接著分析Semaphore的實現(xiàn)原理 Semaphore是什么 Semaphore是一個計數(shù)信號量。Semaphore(信號)可以理解為一種許可,拿到許可的線程才可以繼續(xù)執(zhí)行。Semaphore的計數(shù)器其實記錄的就是許可的數(shù)量,當(dāng)許可數(shù)量為0時,acquire方法...
摘要:簡介抽象隊列同步器,以下簡稱出現(xiàn)在中,由大師所創(chuàng)作。獲取成功則返回,獲取失敗,線程進(jìn)入同步隊列等待。響應(yīng)中斷版的超時響應(yīng)中斷版的共享式獲取同步狀態(tài),同一時刻可能會有多個線程獲得同步狀態(tài)。 1.簡介 AbstractQueuedSynchronizer (抽象隊列同步器,以下簡稱 AQS)出現(xiàn)在 JDK 1.5 中,由大師 Doug Lea 所創(chuàng)作。AQS 是很多同步器的基礎(chǔ)框架,比如 ...
摘要:簡介抽象隊列同步器,以下簡稱出現(xiàn)在中,由大師所創(chuàng)作。獲取成功則返回,獲取失敗,線程進(jìn)入同步隊列等待。響應(yīng)中斷版的超時響應(yīng)中斷版的共享式獲取同步狀態(tài),同一時刻可能會有多個線程獲得同步狀態(tài)。 1.簡介 AbstractQueuedSynchronizer (抽象隊列同步器,以下簡稱 AQS)出現(xiàn)在 JDK 1.5 中,由大師 Doug Lea 所創(chuàng)作。AQS 是很多同步器的基礎(chǔ)框架,比如 ...
閱讀 1467·2023-04-26 00:08
閱讀 797·2021-11-23 18:51
閱讀 1672·2021-11-12 10:34
閱讀 1008·2021-10-14 09:43
閱讀 502·2021-08-18 10:23
閱讀 2581·2019-08-30 15:55
閱讀 3392·2019-08-30 11:05
閱讀 2792·2019-08-29 12:50