面試官:說說Java中的信號量?Semaphore

Semaphore (信號量)是由計算機科學家Dijkstra在1965年提出的,普遍應用不一樣的操做系統,在管程提出以前信號量就是併發編程領域的霸主!幾乎全部併發的語言都支持信號量機制。java

Semaphore 也有被翻譯成信號燈,由於其機制就像咱們平常生活中的紅綠燈,車輛的通行看紅綠燈,對應編程世界的線程能不能執行得看信號燈!面試

Semaphore 用來多線程互斥問題,相對於synchronized和Lock來講它容許多個線程訪問一個臨界區!例如各類池:數據庫鏈接池、對象池等,這些池的需求就是同一時刻容許多個線程同時使用鏈接池。數據庫

Semaphore的模型能夠歸納爲一個計數器、一個等待隊列、三個方法。 三個方法原子性分別是init()、down()、up()編程

init():設置計數器的初始值。bash

down():將計數器的值減一,若是減了一以後,計數器的值小於0,則當前的線程被阻塞,不然繼續執行。多線程

up():將計數器的值加一,若是加了一以後,計數器的值小於等於0,則喚醒等待隊列中的一個線程,而且將它移除出等待隊列。(注意是小於等於0,不該該理解爲大於等於0,由於大於等於0代表此時沒有等待的線程,因此不會有喚醒這個操做。) 併發

簡單的理解就是 Semaphore 就是經過這三個方法來改變計數器,經過計數器的值來判斷此時的線程是應該加入到等待隊列中等待仍是成功執行。分佈式

信號量模型也被稱爲PV原語,也就是downup操做最先稱爲P操做和V操做,有些人還稱爲semWaitsemSignal在JAVA中信號量模型是由 java.util.concurrent.Semaphore 的實現,而且downup對應的實現方法是acquire和release,咱們來看下簡單的使用例子函數

int count;
    final Semaphore semaphore   = new Semaphore(1); // 初始化信號量
    // 用信號量保證互斥    
    void addOne() {
      try {
          semaphore.acquire();   //對應down
          count+=1;
        } catch (InterruptedException e) {
          e.printStackTrace();
        } finally {
          semaphore.release();  //對應up
        }
    }

複製代碼

若是你想多讓幾個線程進去臨界區,那麼就把Semaphore構造器中的1改成你想要的線程數。性能

能夠理解爲頒發許可證,好比想同時容許3個線程進入臨界區,構造器中的數就填3,理解爲搞了3張許可證,而後頒發出去,誰拿到了許可證誰就能進臨界區,進入臨界區後的線程搞完事了,就歸還許可證,而後出去。

Semaphore的內部共存在Sync、NonfairSync、FairSync三個類,NonfairSyncFairSync類繼承自Sync類,Sync類繼承自AbstractQueuedSynchronizer抽象類,也就Semaphore是依託於NonfairSync、FairSync來實現的。

public Semaphore(int permits) {
          sync = new NonfairSync(permits);
      }
      public Semaphore(int permits, boolean fair) {
          sync = fair ? new FairSync(permits) : new NonfairSync(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;
            }
        }
        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);   //非公平
        }
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining)
                    return remaining;
            }
        }
複製代碼

差異就在於有沒hasQueuedPredecessors(),這個方法就是判斷當前線程是不是等待隊列中的頭結點,若是不是,則不給於分配。 大體Semaphore的模型和模型實現思路就是這樣,建議多看看源碼,不難的能夠加深理解,而且懂得具體實現以後能掌握把控更多細節,還不怕面試官問。


若有錯誤歡迎指正!

我的公衆號:yes的練級攻略

有相關面試進階(分佈式、性能調優、經典書籍pdf)資料等待領取

相關文章
相關標籤/搜索