Java的信號(hào)量semaphore講解
一、簡(jiǎn)介
1、Semaphore底層是基于AbstractQueuedSynchronizer來(lái)實(shí)現(xiàn)的。Semaphore稱為計(jì)數(shù)信號(hào)量,它允許n個(gè)任務(wù)同時(shí)訪問(wèn)某個(gè)資源,可以將信號(hào)量看做是在向外分發(fā)使用資源的許可證,只有成功獲取許可證,才能使用資源。
2、它也是通過(guò)內(nèi)部類Sync繼承了AQS,它的方法幾乎都是用的AQS的,少部分重寫(xiě)了AQS的方法,同時(shí),它的信號(hào)量機(jī)制是通過(guò)AQS的state屬性來(lái)操作的,變更state屬性使用到了CAS操作保證變量的數(shù)據(jù)一致性。
3、它的方法幾乎是通過(guò)Sync類,也就是AQS實(shí)現(xiàn)的。
4、該類如果單獨(dú)使用,不涉及到條件隊(duì)列condition,使用到條件隊(duì)列需要調(diào)用await方法、notify方法,notifyAll方法等一系列線程通信的方法,但是,semaphore類中不涉及到這些方法。
5、注意其中一個(gè)方法:release,這個(gè)方法是釋放信號(hào)量到semaphore中,默認(rèn)是釋放一個(gè)。源碼中作者這么說(shuō)的:不要求一個(gè)線程在調(diào)用release方法釋放許可前必須通過(guò)acquire方法獲取到許可。正確的使用semaphore建立在應(yīng)用程序里面,對(duì)許可證的使用,由程序員掌握!semaphore只是提供了一個(gè)信號(hào)量的機(jī)制供其使用。
6、核心方法都在Sync類中,外部方法都是調(diào)用的AQS和Sync中重寫(xiě)的AQS的方法
7、注意其中一個(gè)方法:acquire,這個(gè)方法是獲取信號(hào)量從semaphore中,默認(rèn)是獲取一個(gè)。源碼中作者這么說(shuō)的:如果沒(méi)有可用的許可證,則當(dāng)前線程變?yōu)槌鲇诰€程調(diào)度目的而禁用,并處于休眠狀態(tài),直到發(fā)生兩件事之一:
a、一些其他的線程調(diào)用了release方法釋放了許可,同時(shí)semaphore中的許可滿足當(dāng)前線程,且當(dāng)前線程是下一個(gè)被分配許可證的線程
b、一些其他的線程打斷了當(dāng)前線程且當(dāng)前線程是通過(guò)acquire方式獲取許可的
二、源碼分析
三個(gè)內(nèi)部類
Sync
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(); } //共享模式下非公平策略獲取許可數(shù) final int nonfairTryAcquireShared(int acquires) { for (;;) { //無(wú)限循環(huán) int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) //通過(guò)CAS修改許可數(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)) //通過(guò)CAS修改許可數(shù) 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; } } // 獲取并返回立即可用的所有許可 final int drainPermits() { for (;;) { int current = getState(); if (current == 0 || compareAndSetState(current, 0)) return current; } } }
Sync類繼承了AQS類,重寫(xiě)了nonfairTryAcquireShared、tryReleaseShared方法
NonfairSync和FairSync
//非公平策略 static final class NonfairSync extends Sync { private static final long serialVersionUID = -2694183684443567898L; NonfairSync(int permits) { super(permits); } protected int tryAcquireShared(int acquires) { return nonfairTryAcquireShared(acquires); //調(diào)用的父類Sync的方法 } } //公平策略 static final class FairSync extends Sync { private static final long serialVersionUID = 2014338818796000944L; FairSync(int permits) { super(permits); } //公平策略下子類重寫(xiě)了方法 protected int tryAcquireShared(int acquires) { for (;;) { if (hasQueuedPredecessors()) return -1; int available = getState(); int remaining = available - acquires; if (remaining < 0 || compareAndSetState(available, remaining)) return remaining; } } }
構(gòu)造方法
public Semaphore(int permits) { sync = new NonfairSync(permits); } public Semaphore(int permits, boolean fair) { sync = fair ? new FairSync(permits) : new NonfairSync(permits); }
常用方法
//默認(rèn)獲取一個(gè)許可 public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1); } //默認(rèn)釋放一個(gè)許可 public void release() {sync.releaseShared(1); } //獲取指定的許可數(shù) public void acquire(int permits) throws InterruptedException { if (permits < 0) throw new IllegalArgumentException(); sync.acquireSharedInterruptibly(permits); } //嘗試獲取指定的許可數(shù) public boolean tryAcquire(int permits) { if (permits < 0) throw new IllegalArgumentException(); return sync.nonfairTryAcquireShared(permits) >= 0; } //釋放指定的許可數(shù) public void release(int permits) { if (permits < 0) throw new IllegalArgumentException(); sync.releaseShared(permits); }
沒(méi)啥好說(shuō)的,都是套娃子模式,真正的核心方法在Sync類中以及AQS類
示例
package com.hust.grid.leesf.semaphore; import java.util.concurrent.Semaphore; class MyThread extends Thread { private Semaphore semaphore; public MyThread(String name, Semaphore semaphore) { super(name); this.semaphore = semaphore; } public void run() { int count = 3; System.out.println(Thread.currentThread().getName() + " trying to acquire"); try { semaphore.acquire(count); System.out.println(Thread.currentThread().getName() + " acquire successfully"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(count); System.out.println(Thread.currentThread().getName() + " release successfully"); } } } public class SemaphoreDemo { public final static int SEM_SIZE = 10; public static void main(String[] args) { Semaphore semaphore = new Semaphore(SEM_SIZE); MyThread t1 = new MyThread("t1", semaphore); MyThread t2 = new MyThread("t2", semaphore); t1.start(); t2.start(); int permits = 5; System.out.println(Thread.currentThread().getName() + " trying to acquire"); try { semaphore.acquire(permits); System.out.println(Thread.currentThread().getName() + " acquire successfully"); Thread.sleep(1000); } catch (InterruptedException e) { e.printStackTrace(); } finally { semaphore.release(); System.out.println(Thread.currentThread().getName() + " release successfully"); } } }
說(shuō)明:首先,生成一個(gè)信號(hào)量,信號(hào)量有10個(gè)許可,然后,main,t1,t2三個(gè)線程獲取許可運(yùn)行
首先,main線程執(zhí)行acquire操作,并且成功獲得許可,之后t1線程執(zhí)行acquire操作,成功獲得許可,之后t2執(zhí)行acquire操作,由于此時(shí)許可數(shù)量不夠,t2線程將會(huì)阻塞,直到許可可用。之后t1線程釋放許可,main線程釋放許可,此時(shí)的許可數(shù)量可以滿足t2線程的要求,所以,此時(shí)t2線程會(huì)成功獲得許可運(yùn)行,t2運(yùn)行完成后釋放許可。
三、總結(jié)
1、semaphore類的核心是Sync內(nèi)部類,它繼承了AQS類,適當(dāng)重寫(xiě)了一些方法,其他的方法都調(diào)用的這個(gè)Sync中的方法,包括Sync類的兩個(gè)子類:FairSync和NonFairSync。
2、semaphore類中實(shí)現(xiàn)了公平鎖FairSync和非公平鎖NonFairSync。默認(rèn)使用的是非公平鎖。
3、對(duì)于公平鎖和非公平鎖,主要體現(xiàn)在獲取鎖上面是否公平。非公平鎖不會(huì)判斷當(dāng)前線程是否為同步隊(duì)列中的第一個(gè)節(jié)點(diǎn),而是直接操作state屬性;公平鎖會(huì)判斷當(dāng)前線程是否為同步隊(duì)列中的第一個(gè)節(jié)點(diǎn),具體方法是hasQueuedPredecessors,如果不是,則返回-1,如果是,則執(zhí)行下面步驟。
4、semaphore的信號(hào)量機(jī)制使用的是AQS類的state屬性,默認(rèn)每次獲取或釋放信號(hào)量都是1,除非你指定要使用的信號(hào)量或釋放的信號(hào)量數(shù)。
5、對(duì)state屬性的添加和釋放都必須保證是原子性的,所以,semaphore類中使用的是unsafe類的compareAndSetState方法配合for(; ;)無(wú)限循環(huán),語(yǔ)義為CAS自旋,來(lái)保證對(duì)state屬性的操作是原子性的。
到此這篇關(guān)于Java的信號(hào)量semaphore講解的文章就介紹到這了,更多相關(guān)信號(hào)量semaphore內(nèi)容請(qǐng)搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!
相關(guān)文章
java實(shí)現(xiàn)時(shí)間控制的幾種方案
這篇文章主要介紹了java實(shí)現(xiàn)時(shí)間控制的幾種方案,本文從多個(gè)方面給大家介紹的非常詳細(xì),具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2019-07-07Java實(shí)戰(zhàn)角色權(quán)限后臺(tái)腳手架系統(tǒng)的實(shí)現(xiàn)流程
只學(xué)書(shū)上的理論是遠(yuǎn)遠(yuǎn)不夠的,只有在實(shí)戰(zhàn)中才能獲得能力的提升,本篇文章手把手帶你用java+Springboot+Maven+myBaits-Plus+Vue+Element-UI+Mysql實(shí)現(xiàn)一個(gè)角色權(quán)限后臺(tái)腳手架系統(tǒng),大家可以在過(guò)程中查缺補(bǔ)漏,提升水平2022-01-01java實(shí)現(xiàn)zip,gzip,7z,zlib格式的壓縮打包
本文是利用Java原生類和apache的commons實(shí)現(xiàn)zip,gzip,7z,zlib的壓縮打包,如果你要是感興趣可以進(jìn)來(lái)了解一下。2016-10-10Mybatis基于MapperScan注解的動(dòng)態(tài)代理加載機(jī)制詳解
這篇文章主要介紹了Mybatis基于MapperScan注解的動(dòng)態(tài)代理加載機(jī)制,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)吧2023-01-01解決Springboot項(xiàng)目啟動(dòng)后自動(dòng)創(chuàng)建多表關(guān)聯(lián)的數(shù)據(jù)庫(kù)與表的方案
這篇文章主要介紹了解決Springboot項(xiàng)目啟動(dòng)后自動(dòng)創(chuàng)建多表關(guān)聯(lián)的數(shù)據(jù)庫(kù)與表的方案,本文給大家介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或工作具有一定的參考借鑒價(jià)值,需要的朋友可以參考下2021-03-03