java.util.concurrent 
類 Semaphore

public class Semaphoreextends Objectimplements Serializable一個計數信號量。

從概念上講,信號量維護了一個許可集。

如有必要,在許可可用前會阻塞每一個 acquire(),然后再獲取該許可。
每個 release() 添加一個許可,從而可能釋放一個正在阻塞的獲取者。
但是,不使用實際的許可對象,Semaphore 只對可用許可的號碼進行計數,并采取相應的行動。 

Semaphore 通常用于限制可以訪問某些資源(物理或邏輯的)的線程數目。例如,下面的類使用信號量控制對內容池的訪問: 


 class Pool {
     private static final int MAX_AVAILABLE = 100;
    private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);

    public Object getItem() throws InterruptedException {
          available.acquire();
          return getNextAvailableItem();
  }

   public void putItem(Object x) {
     if (markAsUnused(x))
        available.release();
   }

   // Not a particularly efficient data structure; just for demo

   protected Object[] items =  whatever kinds of items being managed
   protected boolean[] used = new boolean[MAX_AVAILABLE];


   protected synchronized Object getNextAvailableItem() {
     for (int i = 0; i < MAX_AVAILABLE; ++i) {
       if (!used[i]) {
          used[i] = true;
          return items[i];
       }
     }
     return null// not reached
   }

   protected 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;
   }

 }

獲得一項前,每個線程必須從信號量獲取許可,從而保證可以使用該項。

 該線程結束后,將項返回到池中并將許可返回到該信號量,從而允許其他線程獲取該項。

 注意,調用 acquire() 時無法保持同步鎖,因為這會阻止將項返回到池中。
 信號量封裝所需的同步,以限制對池的訪問,這同維持該池本身一致性所需的同步是分開的。 

將信號量初始化為 1,使得它在使用時最多只有一個可用的許可,從而可用作一個相互排斥的鎖。
這通常也稱為二進制信號量,因為它只能有兩種狀態:一個可用的許可,或零個可用的許可。

按此方式使用時,二進制信號量具有某種屬性(與很多 Lock 實現不同),即可以由線程釋放“鎖”,而不是由所有者(因為信號量沒有所有權的概念)。
在某些專門的上下文(如死鎖恢復)中這會很有用。 

此類的構造方法可選地接受一個公平 參數。當設置為 false 時,此類不對線程獲取許可的順序做任何保證。
特別地,闖入 是允許的,也就是說可以在已經等待的線程前為調用 acquire() 的線程分配一個許可,
從邏輯上說,就是新線程將自己置于等待線程隊列的頭部。
當公平設置為 true 時,信號量保證對于任何調用獲取方法的線程而言,都按照處理它們調用這些方法的順序(即先進先出;FIFO)來選擇線程、獲得許可。
注意,FIFO 排序必然應用到這些方法內的指定內部執行點。
所以,可能某個線程先于另一個線程調用了 acquire,但是卻在該線程之后到達排序點,并且從方法返回時也類似。
還要注意,非同步的 tryAcquire 方法不使用公平設置,而是使用任意可用的許可。 

通常,應該將用于控制資源訪問的信號量初始化為公平的,以確保所有線程都可訪問資源。
為其他的種類的同步控制使用信號量時,非公平排序的吞吐量優勢通常要比公平考慮更為重要。 

此類還提供便捷的方法來同時 acquire 和釋放多個許可。
小心,在未將公平設置為 true 時使用這些方法會增加不確定延期的風險。 

內存一致性效果:線程中調用“釋放”方法(比如 release())之前的操作 happen-before 另一線程中緊跟在成功的“獲取”方法(比如 acquire())之后的操作。 

acquire
public void acquire()
             throws InterruptedException從此信號量獲取一個許可,在提供一個許可前一直將線程阻塞,否則線程被中斷。 
獲取一個許可(如果提供了一個)并立即返回,將可用的許可數減 1。 

如果沒有可用的許可,則在發生以下兩種情況之一前,禁止將當前線程用于線程安排目的并使其處于休眠狀態: 

某些其他線程調用此信號量的 release() 方法,并且當前線程是下一個要被分配許可的線程;或者 
其他某些線程中斷當前線程。 
如果當前線程: 

被此方法將其已中斷狀態設置為 on ;或者 
在等待許可時被中斷。 
則拋出 InterruptedException,并且清除當前線程的已中斷狀態。 

拋出: 
InterruptedException - 如果當前線程被中斷

release
public void release()釋放一個許可,將其返回給信號量。 
釋放一個許可,將可用的許可數增加 1。

如果任意線程試圖獲取許可,則選中一個線程并將剛剛釋放的許可給予它。然后針對線程安排目的啟用(或再啟用)該線程。 

不要求釋放許可的線程必須通過調用 acquire() 來獲取許可。通過應用程序中的編程約定來建立信號量的正確用法。 


應用實例:

package com.itm.thread;

/*
 * 
 
 Semaphore的作用類似Lock的功能,不同的是Semaphore的構造函數中可以傳入一個int型的參數,用來確定創建一個多大的通道。
 Lock一次只允許一個線程進入,解鎖后才允許別的線程進入。而Semaphore可以允許多個線程同時進入,一旦有線程釋放就會空出一個位置讓另外的線程進入。
 相當與Lock是一個單車道,而Semaphore是一個多車道。
 
 
*/
import java.util.Random;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Semaphore;

public class SemaphoreTest {
    public static void main(String[] args) {
        ExecutorService es = Executors.newCachedThreadPool();
        
        // final Semaphore sema=new Semaphore(3); //共有3個通道
        final Semaphore sema = new Semaphore(3, true); // 表示按排隊的先后順序進入
        for (int i = 0; i < 10; i++) {
            es.execute(new Runnable() {
                @Override
                public void run() {
                    try {
                        sema.acquire();
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("線程" + Thread.currentThread().getName()
                            + "已進入,當前有" + (3 - sema.availablePermits())
                            + "個線程并發");
                    try {
                        Thread.sleep(new Random().nextInt(5000));
                    } catch (InterruptedException e) {
                        e.printStackTrace();
                    }
                    System.out.println("線程" + Thread.currentThread().getName()
                            + "即將離開");
                    sema.release(); // 一個線程釋放就會允許另外的線程進入
                }
            });
        }
        es.shutdown();
    }
}

運行結果:

線程pool-1-thread-2已進入,當前有2個線程并發
線程pool-1-thread-1已進入,當前有2個線程并發
線程pool-1-thread-3已進入,當前有3個線程并發
線程pool-1-thread-1即將離開
線程pool-1-thread-4已進入,當前有3個線程并發
線程pool-1-thread-4即將離開
線程pool-1-thread-5已進入,當前有3個線程并發
線程pool-1-thread-2即將離開
線程pool-1-thread-9已進入,當前有3個線程并發
線程pool-1-thread-5即將離開
線程pool-1-thread-7已進入,當前有3個線程并發
線程pool-1-thread-3即將離開
線程pool-1-thread-6已進入,當前有3個線程并發
線程pool-1-thread-6即將離開
線程pool-1-thread-8已進入,當前有3個線程并發
線程pool-1-thread-9即將離開
線程pool-1-thread-10已進入,當前有3個線程并發
線程pool-1-thread-7即將離開
線程pool-1-thread-8即將離開
線程pool-1-thread-10即將離開