最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

Java并發(fā)編程之Semaphore詳解

 更新時間:2023年11月20日 08:47:14   作者:西瓜游俠  
這篇文章主要介紹了Java并發(fā)編程之Semaphore詳解,Semaphore信號量可以用來控制同時訪問特定資源的線程數(shù)量,常用于限流場景,Semaphore接收一個int整型值,表示 許可證數(shù)量,需要的朋友可以參考下

1 概念

Semaphore(信號量,發(fā)音:三馬佛兒),可以用來控制同時訪問特定資源的線程數(shù)量,常用于限流場景。

Semaphore接收一個int整型值,表示 許可證數(shù)量。

線程通過調(diào)用acquire()獲取許可證,執(zhí)行完成之后通過調(diào)用release()歸還許可證。只有獲取到許可證的線程才能運(yùn)行,獲取不到許可證的線程將會阻塞。

Semaphore支持公平鎖和非公平鎖。

2 方法

Semaphore提供了一些方法,如下:

方法說明
acquire()獲取一個許可證,在獲取到許可證、或者被其他線程調(diào)用中斷之前線程一直處于阻塞狀態(tài)。
acquire(int permits)一次性獲取多個許可證,在獲取到多個許可證、或者被其他線程調(diào)用中斷、或超時之前線程一直處于阻塞狀態(tài)。
acquireUninterruptibly()獲取一個許可證,在獲取到許可證之前線程一直處于阻塞狀態(tài)(忽略中斷)。
tryAcquire()嘗試獲取許可證,返回獲取許可證成功或失敗,不阻塞線程。
tryAcquire(long timeout, TimeUnit unit)嘗試獲取許可證,在超時時間內(nèi)循環(huán)嘗試獲取,直到嘗試獲取成功或超時返回,不阻塞線程。
release()釋放一個許可證,喚醒等待獲取許可證的阻塞線程。
release(int permits)一次性釋放多個許可證。
drainPermits()清空許可證,把可用許可證數(shù)置為0,返回清空許可證的數(shù)量。

3 例子

public class SemaphoreTest {

    public static void main(String[] args) throws InterruptedException {
        Semaphore semaphore = new Semaphore(5);
        System.out.println("初始總許可數(shù) 5");
        WorkerThread workerThread1 = new WorkerThread("worker-thread-1", semaphore);
        WorkerThread workerThread2 = new WorkerThread("worker-thread-2", semaphore);
        workerThread1.start();
        Thread.sleep(20);
        workerThread2.start();
    }
}

/**
 * 工作線程
 */
class WorkerThread extends Thread {

    private String name;
    private Semaphore semaphore;

    public WorkerThread(String name, Semaphore semaphore) {
        this. name = name;
        this.semaphore = semaphore;
    }

    @Override
    public void run() {
        try {
            System.out.println(this.name + " 嘗試獲取許可.");
            // 獲取許可證
            semaphore.acquire();
            System.out.println(this.name + " 獲取許可成功,當(dāng)前許可還剩 " + semaphore.availablePermits());
            Thread.sleep(3000);
            System.out.println(this.name + " 嘗試釋放許可.");
            // 釋放許可證
            semaphore.release();
            System.out.println(this.name + " 釋放許可成功,當(dāng)前許可還剩 " + semaphore.availablePermits());
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
}

運(yùn)行結(jié)果:

初始總許可數(shù) 5
worker-thread-1 嘗試獲取許可.
worker-thread-2 嘗試獲取許可.
worker-thread-1 獲取許可成功,當(dāng)前許可還剩 4
worker-thread-2 獲取許可成功,當(dāng)前許可還剩 3
worker-thread-1 嘗試釋放許可.
worker-thread-1 釋放許可成功,當(dāng)前許可還剩 4
worker-thread-2 嘗試釋放許可.
worker-thread-2 釋放許可成功,當(dāng)前許可還剩 5
Process finished with exit code 0

4 源碼解析

4.1 構(gòu)造函數(shù)

    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

Semaphore有兩個構(gòu)造函數(shù):

  • 第一個構(gòu)造函數(shù)接收一個int型參數(shù)permits,表示初始化許可證的數(shù)量,并且默認(rèn)使用非公平鎖。
  • 第二個構(gòu)造函數(shù)接收兩個參數(shù),第二個boolean型參數(shù)fair可以用來選擇是使用公平鎖還是非公平鎖。

4.2 Sync、FairSync、NonfairSync

公平鎖FairSync和非公平鎖NonfairSync都繼承了抽象類Sync。

Sync源碼:

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;

        // 設(shè)置同步狀態(tài)的值為初始化的許可證數(shù)量
        Sync(int permits) {
            setState(permits);
        }

        // 獲取同步狀態(tài)的值,也就是剩余可使用的許可證的數(shù)量
        final int getPermits() {
            return getState();
        }

        // 非公平鎖嘗試獲取許可證
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    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ù)量清0
        final int drainPermits() {
            for (;;) {
                int current = getState();
                if (current == 0 || compareAndSetState(current, 0))
                    return current;
            }
        }
    }

Sync繼承AQS抽象類,構(gòu)造函數(shù)調(diào)用的是AQS的setState(int newState)方法,將同步狀態(tài)變量state的值設(shè)置為指定的初始化許可證的數(shù)量。

公平鎖源碼:

    static final class FairSync extends Sync {
        private static final long serialVersionUID = 2014338818796000944L;

        FairSync(int permits) {
            super(permits);
        }

        // 公平鎖獲取許可證
        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;
            }
        }
    }

非公平鎖源碼:

    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);
        }
    }

從源碼中看出,公平鎖和非公平鎖在獲取許可證的時候,邏輯是不一樣的。

4.3 acquire獲取許可證

調(diào)用Semaphore的acquire()函數(shù)可以獲取許可證,源碼如下:

    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

實(shí)際上,調(diào)用的是Sync對象的acquireSharedInterruptibly(int arg)方法,而Sync繼承了AQS,并且沒有重寫這個方法,因此調(diào)用的是AQS的acquireSharedInterruptibly(int arg)方法,源碼如下:

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        // 如果中斷,拋出異常
        if (Thread.interrupted())
            throw new InterruptedException();
        // 嘗試獲取同步變量
        if (tryAcquireShared(arg) < 0)
            // 如果獲取失敗,則將當(dāng)前線程加入同步隊列中去排隊
            doAcquireSharedInterruptibly(arg);
    }

可以看出,調(diào)用tryAcquireShared(arg)來嘗試獲取同步變量,在這里也就是獲取許可證。這個方法在AQS和Sync中都沒有實(shí)現(xiàn),但是被FairSync和NonfairSync分別實(shí)現(xiàn)了。 如果獲取同步變量失敗,則將當(dāng)前線程放入同步隊列中排隊。

4.3.1 公平鎖獲取許可證

如果是FairSync公平鎖,則實(shí)現(xiàn)如下:

    protected int tryAcquireShared(int acquires) {
        // 自旋
        for (;;) {
            // 判斷有沒有等待獲取同步狀態(tài)的線程,有則直接返回-1
            if (hasQueuedPredecessors())
                return -1;
            // 沒有線程在等待獲取同步狀態(tài),那么當(dāng)前線程去獲取同步狀態(tài)
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                // 通過CAS更新同步狀態(tài)的值
                compareAndSetState(available, remaining))
                return remaining;
        }
    }

公平鎖獲取許可證的原理大致如下:

  1. 首先查看有沒有線程在同步隊列中排隊等待獲取許可證,如果有排隊的,那么直接返回-1,這樣將會執(zhí)行doAcquireSharedInterruptibly(arg),將當(dāng)前線程加入同步隊列中去排隊;
  2. 如果沒有線程在排隊,那么當(dāng)前線程獲取同步狀態(tài)的值available,減掉想要獲取的資源值acquires,也就是想要獲取的許可證的數(shù)量,得到剩余的資源量remaining。如果remaining < 0,說明資源不夠,本次獲取失敗,返回remaining值(這個時候返回的是< 0的值),外層代碼會調(diào)用doAcquireSharedInterruptibly(arg)將當(dāng)前線程排隊;如果remaining > 0,說明資源是夠用的,那么直接通過CAS原理更新同步狀態(tài)的值。

4.3.2 非公平鎖獲取許可證

如果是NonfairSync非公平鎖,則實(shí)現(xiàn)如下:

    protected int tryAcquireShared(int acquires) {
        return nonfairTryAcquireShared(acquires);
    }

調(diào)用的是父類Sync的nonfairTryAcquireShared(int acquires)方法:

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

非公平鎖相對來說去掉了查看同步隊列的邏輯。也就是說,在非公平鎖的實(shí)現(xiàn)中,當(dāng)前線程獲取許可證的時候,不用去查看同步隊列是否有線程在等待獲取同步狀態(tài),而是直接去嘗試獲取許可證(改變同步狀態(tài)的值)。

當(dāng)然,如果remaining < 0,說明當(dāng)前線程沒能獲取到期望數(shù)量的許可證,獲取失敗,返回< 0的值,在外部邏輯中,將會調(diào)用doAcquireSharedInterruptibly(arg)使當(dāng)前線程進(jìn)入同步隊列中進(jìn)行等待;如果remaining > 0,則通過CAS原理更新同步狀態(tài)的值。

4.4 release釋放許可證

通過調(diào)用Semaphore的release()方法可以釋放許可證。

    public void release() {
        sync.releaseShared(1);
    }

實(shí)際上,調(diào)用的是Sync的releaseShared(int arg),而Sync并沒有重寫這個方法,因此調(diào)用的是AQS的releaseShared(int arg)方法:

    public final boolean releaseShared(int arg) {
        // 嘗試釋放同步變量
        if (tryReleaseShared(arg)) {
            // 如果成功,則喚醒后繼節(jié)點(diǎn)
            doReleaseShared();
            return true;
        }
        return false;
    }

通過tryReleaseShared(arg)嘗試釋放同步變量,如果成功,則通過doReleaseShared()喚醒后繼節(jié)點(diǎn)。

AQS并沒有實(shí)現(xiàn)tryReleaseShared(arg)方法,而是被Semaphore的Sync實(shí)現(xiàn)了:

    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;
        }
    }

這里通過CAS改變同步狀態(tài)的值,釋放了許可證。

下面來看看doReleaseShared()是如何喚醒后繼節(jié)點(diǎn)的:

    private void doReleaseShared() {
        for (;;) {
            // 首先獲取頭節(jié)點(diǎn)
            Node h = head;
            // 如果頭節(jié)點(diǎn)存在
            if (h != null && h != tail) {
                // 獲取頭節(jié)點(diǎn)的狀態(tài)
                int ws = h.waitStatus;
                // 如果頭節(jié)點(diǎn)的狀態(tài)是Node.SIGNAL,說明頭節(jié)點(diǎn)的后繼節(jié)點(diǎn)正等待被喚醒
                if (ws == Node.SIGNAL) {
                    // 將頭節(jié)點(diǎn)的狀態(tài)設(shè)置為初始狀態(tài)
                    if (!h.compareAndSetWaitStatus(Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    // 喚醒后繼節(jié)點(diǎn)
                    unparkSuccessor(h);
                }
                // 如果頭節(jié)點(diǎn)的狀態(tài)已經(jīng)是0了,則設(shè)置頭節(jié)點(diǎn)狀態(tài)為Node.PROPAGATE
                else if (ws == 0 &&
                         !h.compareAndSetWaitStatus(0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

嘗試喚醒后繼節(jié)點(diǎn)的邏輯比較簡單:

  1. 首先獲取同步隊列中的頭節(jié)點(diǎn);
  2. 如果頭節(jié)點(diǎn)存在,并且不是尾節(jié)點(diǎn),接著獲取頭節(jié)點(diǎn)的狀態(tài);
  3. 如果頭節(jié)點(diǎn)的狀態(tài)是Node.SIGNAL,說明他的后繼節(jié)點(diǎn)正等待著被他喚醒。這個時候通過CAS原理將頭節(jié)點(diǎn)的狀態(tài)置為0,如果成功了,則通過調(diào)用unparkSuccessor(h)喚醒后繼節(jié)點(diǎn),最后實(shí)際上調(diào)用的是LockSupport.unpark(Thread thread)方法喚醒線程的。

到此這篇關(guān)于Java并發(fā)編程之Semaphore詳解的文章就介紹到這了,更多相關(guān)Semaphore詳細(xì)解析內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java判斷List中相同值元素的個數(shù)實(shí)例

    Java判斷List中相同值元素的個數(shù)實(shí)例

    今天小編就為大家分享一篇Java判斷List中相同值元素的個數(shù)實(shí)例,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2018-07-07
  • Spring中屬性注入詳解

    Spring中屬性注入詳解

    這篇文章主要為大家詳細(xì)介紹了Spring中屬性注入,演示了int、String、數(shù)組、list等屬性的注入,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2016-10-10
  • Spring多種加載Bean方式解析

    Spring多種加載Bean方式解析

    本篇文章主要介紹了Spring多種加載Bean方式解析,小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2017-04-04
  • springboot+dubbo實(shí)現(xiàn)時間輪算法

    springboot+dubbo實(shí)現(xiàn)時間輪算法

    時間輪是一種高效利用線程資源進(jìn)行批量化調(diào)度的算法,本文主要介紹了springboot+dubbo實(shí)現(xiàn)時間輪算法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-04-04
  • 使用Feign調(diào)用注解組件(實(shí)現(xiàn)字段賦值功能)

    使用Feign調(diào)用注解組件(實(shí)現(xiàn)字段賦值功能)

    這篇文章主要介紹了使用Feign調(diào)用注解組件(實(shí)現(xiàn)字段賦值功能),具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-03-03
  • 簡單幾步實(shí)現(xiàn)將Spring security4.x升級到5.x

    簡單幾步實(shí)現(xiàn)將Spring security4.x升級到5.x

    這篇文章主要介紹了簡單幾步實(shí)現(xiàn)將Spring security4.x升級到5.x方式,具有很好的參考價值,希望對大家有所幫助,如有錯誤或未考慮完全的地方,望不吝賜教
    2024-08-08
  • springMVC向Controller傳值出現(xiàn)中文亂碼的解決方案

    springMVC向Controller傳值出現(xiàn)中文亂碼的解決方案

    這篇文章主要介紹了springMVC向Controller傳值出現(xiàn)中文亂碼的解決方案,具有很好的參考價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2021-02-02
  • Java使用枚舉替代if/else和switch-case語句的實(shí)踐

    Java使用枚舉替代if/else和switch-case語句的實(shí)踐

    在軟件開發(fā)中if-else和switch-case語句經(jīng)常被用來處理不同的條件分支,但在大型項目中,這種做法可能導(dǎo)致代碼可讀性差、維護(hù)困難,這篇文章主要給大家介紹了關(guān)于Java使用枚舉替代if/else和switch-case語句的相關(guān)資料,需要的朋友可以參考下
    2024-09-09
  • Mybatis的TypeHandler實(shí)現(xiàn)數(shù)據(jù)加解密詳解

    Mybatis的TypeHandler實(shí)現(xiàn)數(shù)據(jù)加解密詳解

    這篇文章主要介紹了Mybatis基于TypeHandler實(shí)現(xiàn)敏感數(shù)據(jù)加密詳解,Typehandler是mybatis提供的一個接口,通過實(shí)現(xiàn)這個接口,可以實(shí)現(xiàn)jdbc類型數(shù)據(jù)和java類型數(shù)據(jù)的轉(zhuǎn)換,需要的朋友可以參考下
    2024-01-01
  • @Autowired 自動注入接口失敗的原因及解決

    @Autowired 自動注入接口失敗的原因及解決

    這篇文章主要介紹了@Autowired 自動注入接口失敗的原因及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-02-02

最新評論

夏津县| 基隆市| 屯留县| 荔浦县| 金坛市| 内丘县| 新余市| 永年县| 乐至县| 遂川县| 宜川县| 永善县| 闵行区| 弥渡县| 昌图县| 沙坪坝区| 连城县| 天峻县| 五指山市| 京山县| 永清县| 虹口区| 拉萨市| 彭山县| 喀喇| 金堂县| 万源市| 共和县| 正定县| 惠来县| 公主岭市| 高安市| 河间市| 定南县| 石门县| 保康县| 辽宁省| 乌苏市| 濮阳县| 冷水江市| 香港 |