1. 程式人生 > >Java併發程式設計(8)-Semaphore訊號量解讀

Java併發程式設計(8)-Semaphore訊號量解讀

文章目錄


更多關於Java併發程式設計的文章請點選這裡:Java併發程式設計實踐(0)-目錄頁


在Java併發程式設計中,訊號量Semaphore類)可視為synchronized的升級,它能控制一個程式中執行緒的數量,負責協調各個執行緒, 以保證它們能夠正確、合理的使用公共資源。

一、Semaphore訊號量

1.1、什麼是訊號量

以一個停車場是運作為例。為了簡單起見,假設停車場只有三個車位,一開始三個車位都是空的。這時如果同時來了五輛車,看門人允許其中三輛不受阻礙的進入,然後放下車攔,剩下的車則必須在入口等待,此後來的車也都不得不在入口處等待。這時,有一輛車離開停車場,看門人得知後,開啟車攔,放入一輛,如果又離開兩輛,則又可以放入兩輛,如此往復。
在這個停車場系統中,車位是公共資源,每輛車好比一個執行緒,看門人起的就是訊號量的作用。
更進一步,訊號量的特性如下:訊號量是一個非負整數(車位數),所有通過它的執行緒(車輛)都會將該整數減一(通過它當然是為了使用資源),當該整數值為零時,所有試圖通過它的執行緒都將處於等待狀態。在訊號量上我們定義兩種操作: Wait(等待) 和 Release(釋放)。 當一個執行緒呼叫Wait(等待)操作時,它要麼通過然後將訊號量減一,要麼一直等下去,直到訊號量大於一或超時。Release(釋放)實際上是在訊號量上執行加操作,對應於車輛離開停車場,該操作之所以叫做“釋放”是因為加操作實際上是釋放了由訊號量守護的資源。
在java中,還可以設定該訊號量是否採用公平模式,如果以公平方式執行,則執行緒將會按到達的順序(FIFO)執行,如果是非公平,則可以後請求的有可能排在佇列的頭部


JDK中定義如下:
Semaphore(int permits, boolean fair)
  建立具有給定的許可數和給定的公平設定的Semaphore。 --百度百科

1.2、訊號量在併發程式設計中的作用

在Java併發程式設計中,訊號量Semaphore類)可視為synchronized的升級,它能控制一個程式中執行緒的數量,負責協調各個執行緒, 以保證它們能夠正確、合理的使用公共資源。

二、Semaphore類簡單解讀

Semaphore類位於java.util.concurrent併發包之下,它是訊號量的具體實現類。

2.1、構造方法解讀

(1)、public Semaphore(int permits)


用給定的許可數和非公平的公平設定建立一個 Semaphore,即執行緒不按照FIFO的方式執行。

引數:
permits - 初始的可用許可數目。此值可能為負數,在這種情況下,必須在授予任何獲取前進行釋放。

(2)、public Semaphore(int permits, boolean fair)
用給定的許可數和給定的公平設定建立一個 Semaphore,保證執行緒的執行是FIFO方式的。

引數:
permits - 初始的可用許可數目。此值可能為負數,在這種情況下,必須在授予任何獲取前進行釋放。
fair - 如果此訊號量保證在爭用時按先進先出的順序授予許可,則為 ture,否則為 false。

2.2、常用方法解讀

(1)、acquire方法
public void acquire() throws InterruptedException
從此訊號量獲取一個許可,在提供一個許可前一直將執行緒阻塞,否則執行緒被中斷。 獲取一個許可(如果提供了一個)並立即返回,將可用的許可數減 1。
如果沒有可用的許可,則在發生以下兩種情況之一前,禁止將當前執行緒用於執行緒安排目的並使其處於休眠狀態:

某些其他執行緒呼叫此訊號量的 release() 方法,並且當前執行緒是下一個要被分配許可的執行緒;
②其他某些執行緒中斷當前執行緒

(2)、tryAcquire方法
public boolean tryAcquire()
僅在呼叫時此訊號量存在一個可用許可,才從訊號量獲取許可。
獲取一個許可(如果提供了一個)並立即返回,其值為 true,將可用的許可數減 1。 如果沒有可用的許可,則此方法立即返回並且值為 false。

public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException
如果在給定的等待時間內此訊號量有可用的所有許可,並且當前執行緒未被中斷,則從此訊號量獲取給定數目的許可。
獲取給定數目的許可(如果提供了)並立即返回,其值為 true,將可用的許可數減去給定的量。

如果沒有足夠的可用許可,則在發生以下三種情況之一前,禁止將當前執行緒用於執行緒安排目的並使其處於休眠狀態:

①其他某些執行緒呼叫此訊號量的某個釋放方法,當前執行緒是下一個被分配許可的執行緒,並且可用許可的數目滿足此請求;

②其他某些執行緒中斷當前執行緒;

③ 已超出指定的等待時間。

(3)、availablePermits方法
public int availablePermits()
返回此訊號量中當前可用的許可數。 此方法通常用於除錯和測試目的。

(4)、release方法
public void release(int permits)
釋放給定數目的許可,將其返回到訊號量。
釋放給定數目的許可,將可用的許可數增加該量。如果任意執行緒試圖獲取許可,則選中某個執行緒並將剛剛釋放的許可給予該執行緒。如果可用許可的數目滿足該執行緒的請求,則針對執行緒安排目的啟用(或再啟用)該執行緒;否則在有足夠的可用許可前執行緒將一直等待。如果滿足此執行緒的請求後仍有可用的許可,則依次將這些許可分配給試圖獲取許可的其他執行緒。

三、簡單程式解讀

@Test
    public void semaphoreTest(){
        Semaphore semaphore = new Semaphore(3);

        ExecutorService executorService = Executors.newFixedThreadPool(2);
        for (int i = 0; i < 5; i++) {
            executorService.execute(new Runnable(){
                Semaphore semaphore1 = semaphore;
                public void run(){

                    try {
                        semaphore1.acquire();//獲取訊號量,執行程式
                        System.out.println(Thread.currentThread().getName()+"獲得訊號量,開始執行程式...,剩餘訊號量:"+semaphore1.availablePermits());
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }finally {
                        semaphore1.release();//釋放訊號量
                        System.out.println("執行緒"+Thread.currentThread().getName()+"釋放訊號量,剩餘訊號量:"+semaphore1.availablePermits());
                    }

                }
            });
        }
    }

在這裡插入圖片描述如果我們不釋放訊號量。那麼3訊號量被消耗完,程式也就是迴圈到第3次的時候,程式就已經結束了,因為沒有訊號量,執行緒也就沒有權利執行程式。

		finally {
                       // semaphore1.release();//釋放訊號量
                        //System.out.println("執行緒"+Thread.currentThread().getName()+"釋放訊號量,剩餘訊號量:"+semaphore1.availablePermits());
                        System.out.println("剩餘訊號量:"+semaphore1.availablePermits());

                    }

在這裡插入圖片描述