Java併發編程(8)-Semaphore信號量解讀


一、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());

                    }

在這裏插入圖片描述

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章