JAVA Semaphore詳解


  Semaphore(信號量):是一種計數器,用來保護一個或者多個共享資源的訪問。如果線程要訪問一個資源就必須先獲得信號量。如果信號量內部計數器大於0,信號量減1,然后允許共享這個資源;否則,如果信號量的計數器等於0,信號量將會把線程置入休眠直至計數器大於0.當信號量使用完時,必須釋放。

實例代碼:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
         final Semaphore semaphore = new Semaphore( 2 );
         ExecutorService executorService = Executors.newCachedThreadPool();
         for ( int i = 0 ; i < 10 ; i++) {
             final int index = i;
             executorService.execute( new Runnable() {
                 public void run() {
                     try {
                         semaphore.acquire();
                         System.out.println( "線程:" + Thread.currentThread().getName() + "獲得許可:" + index);
                         TimeUnit.SECONDS.sleep( 1 );
                         semaphore.release();
                         System.out.println( "允許TASK個數:" + semaphore.availablePermits()); 
                     } catch (InterruptedException e) {
                         e.printStackTrace();
                     }
                 }
             });
         }
         executorService.shutdown();

  構造方法1:

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

  permits 初始許可數,也就是最大訪問線程數構造方法2:

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

  permits 初始許可數,也就是最大訪問線程數

  fair 當設置為false時,創建的信號量為非公平模式;當設置為true時,信號量是公平模式

主要方法:

  • void acquire() :從信號量獲取一個許可,如果無可用許可前將一直阻塞等待,
  • void acquire(int permits) :獲取指定數目的許可,如果無可用許可前也將會一直阻塞等待
  • boolean tryAcquire():從信號量嘗試獲取一個許可,如果無可用許可,直接返回false,不會阻塞
  • boolean tryAcquire(int permits): 嘗試獲取指定數目的許可,如果無可用許可直接返回false
  • boolean tryAcquire(int permits, long timeout, TimeUnit unit): 在指定的時間內嘗試從信號量中獲取許可,如果在指定的時間內獲取成功,返回true,否則返回false
  • void release(): 釋放一個許可,別忘了在finally中使用,注意:多次調用該方法,會使信號量的許可數增加,達到動態擴展的效果,如:初始permits為1, 調用了兩次release,最大許可會改變為2
  • int availablePermits(): 獲取當前信號量可用的許可

JDK 非公平Semaphore實現:

  1.使用一個參數的構造創建Semaphore對象時,會創建一個NonfairSync對象實例,並將state值設為傳入的值(permits ),

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

  NonfairSync間接的繼承了AbstractQueuedSynchronizer實現

1
2
3
4
5
6
7
8
9
10
11
final static class NonfairSync extends Sync {
         private static final long serialVersionUID = -2694183684443567898L;
 
         NonfairSync( int permits) {
             super (permits);
         }
 
         protected int tryAcquireShared( int acquires) {
             return nonfairTryAcquireShared(acquires);
         }
     }
1
2
3
4
5
6
abstract static class Sync extends AbstractQueuedSynchronizer {
     private static final long serialVersionUID = 1192457210091910933L;
 
     Sync( int permits) {
         setState(permits);
     }

  AbstractQueuedSynchronizer 的setState方法

1
2
3
protected final void setState( int newState) {
     state = newState;
}

 

  2.調用tryAcquire方法時,實際是調用NonfairSync的nonfairTryAcquireShared方法,nonfairTryAcquireShared在父類Sync中實現,

Semaphore# tryAcquire方法:

1
2
3
public boolean tryAcquire() {
     return sync.nonfairTryAcquireShared( 1 ) >= 0 ;
}


  Sync的nonfairTryAcquireShared方法

1
2
3
4
5
6
7
8
9
final int nonfairTryAcquireShared( int acquires) {
     for (;;) {
         int available = getState();
         int remaining = available - acquires;
         if (remaining < 0 ||
             compareAndSetState(available, remaining))
             return remaining;
     }
}

  nonfairTryAcquireShared 方法通過獲取當前的state,以此state減去需要獲取信號量的個數,作為剩余個數,如果結果小於0,返回此剩余的個數;如果結果大於等於0,則基於 CAS將state的值設置為剩余個數,當前步驟用到了for循環,所以只有在結果小於0或設置state值成功的情況下才會退出。如果返回的剩余許可個數大於0,tryAcquire方法則返回true;其余返回false。

  AbstractQueuedSynchronizer的compareAndSetState方法,

1
2
3
4
protected final boolean compareAndSetState( int expect, int update) {
     // See below for intrinsics setup to support this
     return unsafe.compareAndSwapInt( this , stateOffset, expect, update);
}


  3.release方法,釋放一個許可

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

  AbstractQueuedSynchronizer的releaseShared方法,

1
2
3
4
5
6
7
public final boolean releaseShared( int arg) {
     if (tryReleaseShared(arg)) {
         doReleaseShared();
         return true ;
     }
     return false ;
}

  release方法間接的調用了Sync的tryReleaseShared方法,該方法基於Cas 將state的值設置為state+1,一直循環確保CAS操作成功,成功后返回true。

1
2
3
4
5
6
7
protected final boolean tryReleaseShared( int releases) {
     for (;;) {
         int p = getState();
         if (compareAndSetState(p, p + releases))
             return true ;
     }
}

  根據上面分析,可以看得出,Semaphore采用了CAS來實現,盡量避免鎖的使用,提高了性能


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM