成人无码视频,亚洲精品久久久久av无码,午夜精品久久久久久毛片,亚洲 中文字幕 日韩 无码

資訊專欄INFORMATION COLUMN

Java多線程進階(二十)—— J.U.C之synchronizer框架:Semaphore

boredream / 2887人閱讀

摘要:當線程使用完共享資源后,可以歸還許可,以供其它需要的線程使用。所以,并不會阻塞調用線程。立即減少指定數目的可用許可數。方法用于將可用許可數清零,并返回清零前的許可數六的類接口聲明類聲明構造器接口聲明

本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog...
一、Semaphore簡介

Semaphore,又名信號量,這個類的作用有點類似于“許可證”。有時,我們因為一些原因需要控制同時訪問共享資源的最大線程數量,比如出于系統性能的考慮需要限流,或者共享資源是稀缺資源,我們需要有一種辦法能夠協調各個線程,以保證合理的使用公共資源。

Semaphore維護了一個許可集,其實就是一定數量的“許可證”。
當有線程想要訪問共享資源時,需要先獲取(acquire)的許可;如果許可不夠了,線程需要一直等待,直到許可可用。當線程使用完共享資源后,可以歸還(release)許可,以供其它需要的線程使用。

另外,Semaphore支持公平/非公平策略,這和ReentrantLock類似,后面講Semaphore原理時會看到,它們的實現本身就是類似的。

二、Semaphore示例

我們來看下Oracle官方給出的示例:

class Pool {
    private static final int MAX_AVAILABLE = 100; // 可同時訪問資源的最大線程數
    private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
    protected Object[] items = new Object[MAX_AVAILABLE];   //共享資源
    protected boolean[] used = new boolean[MAX_AVAILABLE];
    public Object getItem() throws InterruptedException {
        available.acquire();
        return getNextAvailableItem();
    }
    public void putItem(Object x) {
        if (markAsUnused(x))
            available.release();
    }
    private synchronized Object getNextAvailableItem() {
        for (int i = 0; i < MAX_AVAILABLE; ++i) {
            if (!used[i]) {
                used[i] = true;
                return items[i];
            }
        }
        return null;
    }
    private synchronized boolean markAsUnused(Object item) {
        for (int i = 0; i < MAX_AVAILABLE; ++i) {
            if (item == items[i]) {
                if (used[i]) {
                    used[i] = false;
                    return true;
                } else
                    return false;
            }
        }
        return false;
    }
}

items數組可以看成是我們的共享資源,當有線程嘗試使用共享資源時,我們要求線程先獲得“許可”(調用Semaphoreacquire方法),這樣線程就擁有了權限,否則就需要等待。當使用完資源后,線程需要調用Semaphorerelease方法釋放許可。

 注意:上述示例中,對于共享資源訪問需要由鎖來控制,Semaphore僅僅是保證了線程由權限使用共享資源,至于使用過程中是否由并發(fā)問題,需要通過鎖來保證。

總結一下,許可數 ≤ 0代表共享資源不可用。許可數 > 0,代表共享資源可用,且多個線程可以同時訪問共享資源。

這是不是和CountDownLatch有點像?
我們來比較下:

同步器 作用
CountDownLatch 同步狀態(tài)State > 0表示資源不可用,所有線程需要等待;State == 0表示資源可用,所有線程可以同時訪問
Semaphore 剩余許可數 < 0表示資源不可用,所有線程需要等待; 許可剩余數 ≥ 0表示資源可用,所有線程可以同時訪問
如果讀者閱讀過本系列的AQS相關文章,應該立馬可以反應過來,這其實就是對同步狀態(tài)的定義不同。
CountDownLatch內部實現了AQS的共享功能,那么Semaphore是否也一樣是利用內部類實現了AQS的共享功能呢?
三、Semaphore原理 Semaphore的內部結構

我們先來看下Semaphore的內部:

可以看到,Semaphore果然是通過內部類實現了AQS框架提供的接口,而且基本結構幾乎和ReentrantLock完全一樣,通過內部類分別實現了公平/非公平策略。

Semaphore對象的構造

Semaphore sm = new Semaphore (3, true);

Semaphore有兩個構造器:

構造器1:

構造器2:

構造時需要指定“許可”的數量——permits,內部結構如下:

四、Semaphore的公平策略分析

我們還是通過示例來分析:

假設現在一共3個線程:ThreadA、ThreadB、ThreadC。一個許可數為2的公平策略的Semaphore。線程的調用順序如下:
Semaphore sm = new Semaphore (2, true);

// ThreadA: sm.acquire()

// ThreadB: sm.acquire(2)

// ThreadC: sm.acquire()

// ThreadA: sm.release()

// ThreadB: sm.release(2)
創(chuàng)建公平策略的Semaphore對象
Semaphore sm = new Semaphore (2, true);

可以看到,內部創(chuàng)建了一個FairSync對象,并傳入許可數permits

SyncSemaphore的一個內部抽象類,公平策略的FairSync和非公平策略的NonFairSync都繼承該類。
可以看到,構造器傳入的permits值就是同步狀態(tài)的值,這也體現了我們在AQS系列中說過的:
AQS框架的設計思想就是分離構建同步器時的一系列關注點,它的所有操作都圍繞著資源——同步狀態(tài)(synchronization state)來展開,并將資源的定義和訪問留給用戶解決:

ThreadA調用acqure方法

Semaphoreacquire方法內部調用了AQS的方法,入參"1"表示嘗試獲取1個許可:

AQS的acquireSharedInterruptibly方式是共享功能的一部分,我們在AQS系列中就已經對它很熟悉了:

關鍵來看下Semaphore是如何實現tryAcquireShared方法的:

對于Semaphore來說,線程是可以一次性嘗試獲取多個許可的,此時只要剩余的許可數量夠,最終會通過自旋操作更新成功。如果剩余許可數量不夠,會返回一個負數,表示獲取失敗。

顯然,ThreadA獲取許可成功。此時,同步狀態(tài)值State == 1,等待隊列的結構如下:

ThreadB調用acqure(2)方法

帶入參的aquire方法內部和無參的一樣,都是調用了AQS的acquireSharedInterruptibly方法:

此時,ThreadB一樣進入tryAcquireShared方法。不同的是,此時剩余許可數不足,因為ThreadB一次性獲取2個許可,tryAcquireShared方法返回一個負數,表示獲取失敗:
remaining = available - acquires = 1- 2 = -1;

ThreadB會調用doAcquireSharedInterruptibly方法:

上述方法首先通過addWaiter方法將ThreadB包裝成一個共享結點,加入等待隊列:

然后會進入自旋操作,先嘗試獲取一次資源,顯然此時是獲取失敗的,然后判斷是否要進入阻塞(shouldParkAfterFailedAcquire):

上述方法會先將前驅結點的狀態(tài)置為SIGNAL,表示ThreadB需要阻塞,但在阻塞之前需要將前驅置為SIGNAL,以便將來可以喚醒ThreadB。

最終ThreadB會在parkAndCheckInterrupt中進入阻塞:

此時,同步狀態(tài)值依然是State == 1,等待隊列的結構如下:

ThreadC調用acqure()方法

流程和步驟3完全相同,ThreadC被包裝成結點加入等待隊列后:

同步狀態(tài):State == 1

ThreadA調用release()方法

Semaphorerealse方法調用了AQS的releaseShared方法,默認入參為"1",表示歸還一個許可:

來看下Semaphore是如何實現tryReleaseShared方法的,tryReleaseShared方法是一個自旋操作,直到更新State成功:

更新完成后,State == 2,ThreadA會進入doReleaseShared方法,先將頭結點狀態(tài)置為0,表示即將喚醒后繼結點:

此時,等待隊列結構:

然后調用unparkSuccessor方法喚醒后繼結點:

此時,ThreadB被喚醒,會從原阻塞處繼續(xù)向下執(zhí)行:

此時,同步狀態(tài):State == 2

ThreadB從原阻塞處繼續(xù)執(zhí)行

ThreadB被喚醒后,從下面開始繼續(xù)往下執(zhí)行,進入下一次自旋:

在下一次自旋中,ThreadB調用tryAcquireShared方法成功獲取到共享資源(State修改為0),setHeadAndPropagate方法把ThreadB變?yōu)轭^結點,
并根據傳播狀態(tài)判斷是否要喚醒并釋放后繼結點:

同步狀態(tài):State == 0

ThreadB會調用doReleaseShared方法,繼續(xù)嘗試喚醒后繼的共享結點(也就是ThreadC),這個過程和ThreadB被喚醒完全一樣:

同步狀態(tài):State == 0

ThreadC從原阻塞處繼續(xù)執(zhí)行

由于目前共享資源仍為0,所以ThreadC被喚醒后,在經過嘗試獲取資源失敗后,又進入了阻塞:

ThreadA調用release(2)方法

內部和無參的release方法一樣:

更新完成后,State == 2,ThreadA會進入doReleaseShared方法,喚醒后繼結點:

此時,等待隊列結構:

同步狀態(tài):State == 2

ThreadC從原阻塞處繼續(xù)執(zhí)行

由于目前共享資源為2,所以ThreadC被喚醒后,獲取資源成功:

最終同步隊列的結構如下:

同步狀態(tài):State == 0

五、總結

Semaphore其實就是實現了AQS共享功能的同步器,對于Semaphore來說,資源就是許可證的數量:

剩余許可證數(State值) - 嘗試獲取的許可數(acquire方法入參) ≥ 0:資源可用

剩余許可證數(State值) - 嘗試獲取的許可數(acquire方法入參) < 0:資源不可用

這里共享的含義是多個線程可以同時獲取資源,當計算出的剩余資源不足時,線程就會阻塞。

注意:Semaphore不是鎖,只能限制同時訪問資源的線程數,至于對數據一致性的控制,Semaphore是不關心的。當前,如果是只有一個許可的Semaphore,可以當作鎖使用。
Semaphore的非公平策略

另外,上述我們討論的是Semaphore的公平策略,非公平策略的差異并不大:

可以看到,非公平策略不會去查看等待隊列的隊首是否有其它線程正在等待,而是直接嘗試修改State值。

Semaphore的其它方法

Semaphore還有兩個比較特殊的方法,這兩個方法的特點是采用自旋操作State變量,直到成功為止。所以,并不會阻塞調用線程。

reducePermits

reducePermits立即減少指定數目的可用許可數。

drainPermits

drainPermits方法用于將可用許可數清零,并返回清零前的許可數

六、Semaphore的類/接口聲明 類聲明

構造器

接口聲明


文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規(guī)行為,您可以聯系管理員刪除。

轉載請注明本文地址:http://m.hztianpu.com/yun/76652.html

相關文章

  • Java線程進階(一)—— J.U.C并發(fā)包概述

    摘要:整個包,按照功能可以大致劃分如下鎖框架原子類框架同步器框架集合框架執(zhí)行器框架本系列將按上述順序分析,分析所基于的源碼為。后,根據一系列常見的多線程設計模式,設計了并發(fā)包,其中包下提供了一系列基礎的鎖工具,用以對等進行補充增強。 showImg(https://segmentfault.com/img/remote/1460000016012623); 本文首發(fā)于一世流云專欄:https...

    anonymoussf 評論0 收藏0
  • Java線程進階(五)—— J.U.Clocks框架:LockSupport

    摘要:初始時,為,當調用方法時,線程的加,當調用方法時,如果為,則調用線程進入阻塞狀態(tài)。該對象一般供監(jiān)視診斷工具確定線程受阻塞的原因時使用。 showImg(https://segmentfault.com/img/remote/1460000016012503); 本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog... 一、LockSupport類簡介...

    jsyzchen 評論0 收藏0
  • Java線程進階(六)—— J.U.Clocks框架:AQS綜述(1)

    摘要:在時,引入了包,該包中的大多數同步器都是基于來構建的。框架提供了一套通用的機制來管理同步狀態(tài)阻塞喚醒線程管理等待隊列。指針用于在結點線程被取消時,讓當前結點的前驅直接指向當前結點的后驅完成出隊動作。 showImg(https://segmentfault.com/img/remote/1460000016012438); 本文首發(fā)于一世流云的專欄:https://segmentfau...

    cocopeak 評論0 收藏0
  • Java線程進階(十八)—— J.U.Csynchronizer框架:CountDownLatc

    摘要:線程可以調用的方法進入阻塞,當計數值降到時,所有之前調用阻塞的線程都會釋放。注意的初始計數值一旦降到,無法重置。 showImg(https://segmentfault.com/img/remote/1460000016012041); 本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog... 一、CountDownLatch簡介 CountDow...

    Elle 評論0 收藏0
  • Java線程進階(二)—— J.U.Clocks框架:接口

    摘要:二接口簡介可以看做是類的方法的替代品,與配合使用。當線程執(zhí)行對象的方法時,當前線程會立即釋放鎖,并進入對象的等待區(qū),等待其它線程喚醒或中斷。 showImg(https://segmentfault.com/img/remote/1460000016012601); 本文首發(fā)于一世流云的專欄:https://segmentfault.com/blog... 本系列文章中所說的juc-...

    dkzwm 評論0 收藏0

發(fā)表評論

0條評論

boredream

|高級講師

TA的文章

閱讀更多
最新活動
閱讀需要支付1元查看
<