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即將離開