java.util.concurrent
类 Semaphore
java.lang.Object
继承者 java.util.concurrent.Semaphore
所有已实现的接口:
Serializable
public class Semaphore
extends
Object
implements
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())之后的操作。
构造方法摘要
Semaphore(int permits)
创建具有给定的许可数和非公平的公平设置的 Semaphore。
Semaphore(int permits, boolean fair)
创建具有给定的许可数和给定的公平设置的 Semaphore。
void acquire()
从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。
void acquire(int permits)
从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞,或者线程已被中断。
void acquireUninterruptibly()
从此信号量中获取许可,在有可用的许可前将其阻塞。
void acquireUninterruptibly(int permits)
从此信号量获取给定数目的许可,在提供这些许可前一直将线程阻塞。
int availablePermits()
返回此信号量中当前可用的许可数。
int drainPermits()
获取并返回立即可用的所有许可。
protected Collection getQueuedThreads()
返回一个 collection,包含可能等待获取的线程。
int getQueueLength()
返回正在等待获取的线程的估计数目。
boolean hasQueuedThreads()
查询是否有线程正在等待获取。
boolean isFair()
如果此信号量的公平设置为 true,则返回 true。
protected void reducePermits(int reduction)
根据指定的缩减量减小可用许可的数目。
void release()
释放一个许可,将其返回给信号量。
Seamaphore也是一个线程听不辅助类,可以维护当前访问自身的线程个数,并提供了同步机制,使用Seamaphore可以控制同时访问资源的线程个数,例如,实现一个文件循序的并发访问数
使用信号量控制:
package Thread;
import java.util.concurrent.Semaphore;
public class ABC2 {
private static Semaphore A=new Semaphore(1);
private static Semaphore B=new Semaphore(1);
private static Semaphore C=new Semaphore(1);
static class ThreadA extends Thread{
@Override
public void run() {
try {
for(int i=0;i<10;i++){
A.acquire();
System.out.print("A ");
B.release();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
static class ThreadB extends Thread{
@Override
public void run() {
try {
for(int i=0;i<10;i++){
B.acquire();
System.out.print("B ");
C.release();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
static class ThreadC extends Thread{
@Override
public void run() {
try {
for(int i=0;i<10;i++){
C.acquire();
System.out.println(“C”);
A.release();
}
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
public static void main(String[] args) throws InterruptedException {
B.acquire();
C.acquire();
new ThreadA().start();
new ThreadB().start();
new ThreadC().start();
}
}