一般的锁在任意时刻只允许一个线程访问一项资源,而计数信号量允许n个任务同时访问一项资源。我们可以将信号量看做一个许可集,可以向线程分发使用资源的许可证。获得资源前,线程调用acquire()从许可集中获取许可。该线程结束后,通过release()将许可还给许可集。
函数列表
//构造方法摘要
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<Thread> getQueuedThreads()
//返回一个 collection,包含可能等待获取的线程。
int getQueueLength()
//返回正在等待获取的线程的估计数目。
boolean hasQueuedThreads()
//查询是否有线程正在等待获取。
boolean isFair()
//如果此信号量的公平设置为 true,则返回 true。
protected void reducePermits(int reduction)
//根据指定的缩减量减小可用许可的数目。
void release()
//释放一个许可,将其返回给信号量。
void release(int permits)
//释放给定数目的许可,将其返回到信号量。
String toString()
//返回标识此信号量的字符串,以及信号量的状态。
boolean tryAcquire()
//仅在调用时此信号量存在一个可用许可,才从信号量获取许可。
boolean tryAcquire(int permits)
//仅在调用时此信号量中有给定数目的许可时,才从此信号量中获取这些许可。
boolean tryAcquire(int permits, long timeout, TimeUnit unit)
//如果在给定的等待时间内此信号量有可用的所有许可,并且当前线程未被中断,则从此信号量获取给定数目的许可。
boolean tryAcquire(long timeout, TimeUnit unit)
//如果在给定的等待时间内,此信号量有可用的许可并且当前线程未被中断,则从此信号量获取一个许可。
此类的构造方法可选地接受一个公平参数fair。当fair设置为false时,此类不对线程获取许可的顺序做任何保证,也就是说可以在已经等待的线程前为调用acquire() 的线程分配一个许可。当公平设置为true时,信号量保证对于任何调用acquire()的线程而言,都按照FIFO的规则来选择线程、获得许可。注意,FIFO排序必然应用到这些方法内的指定内部执行点。所以,可能某个线程先于另一个线程调用了 acquire(),但是却在该线程之后到达排序点,并且从方法返回时也类似。还要注意,非同步的tryAcquire()方法不使用公平设置,而是使用任意可用的许可。
通常,应该将用于控制资源访问的信号量初始化为公平的,以确保所有线程都可访问资源。为其他的种类的同步控制使用信号量时,非公平排序的吞吐量优势通常要比公平考虑更为重要。
例1:信号量的简单使用
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 exec = Executors.newCachedThreadPool();
// 创建许可数为3和非公平的公平设置的 Semaphore。
final Semaphore semp = new Semaphore(3);
// 模拟10个客户端访问
for (int index = 0; index < 10; index++) {
Runnable run = new Runnable() {
public void run() {
try {
// 从此信号量获取一个许可
semp.acquire();
System.out.println(Thread.currentThread().getName());
} catch (InterruptedException e) {
} finally {
// 释放信号量。如果没有这条语句,则在控制台只能打印5条记录,之后线程一直阻塞
semp.release();
}
}
};
exec.execute(run);
}
exec.shutdown();
}
}
源码
待补充
本文就讲到这里,想了解Java并发编程更多内容请参考:
- Java并发编程札记-目录