Semaphore信号量

Semaphore为并发包(java.util.concurrent)中提供用于控制某资源同时可以被几个线程访问的类

用途

信号量主要有两种用途:
保护一个重要(代码)部分防止一次超过 N 个线程进入。
在两个线程之间发送信号。

构造方法

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

permits 初始许可数,也就是最大访问线程数4

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

permits 初始许可数,也就是最大访问线程数
fair 当设置为false时,线程获取许可的顺序是无序的,也就是说新线程可能会比等待的老线程会先获得许可;当设置为true时,信号量保证它们调用的顺序(即先进先出;FIFO)

主要方法

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() 获取当前信号量可用的许可

用法

保护重要部分

如果你将信号量用于保护一个重要部分,试图进入这一部分的代码通常会首先尝试获得一个许可,然后才能进入重要部分(代码块),执行完之后,再把许可释放掉,例如:

1
2
3
4
5
6
7
8
9
10
11
Semaphore semaphore = new Semaphore(5);  

//如果没有许可将一直阻塞
semaphore.acquire();
...
semaphore.release();

//尝试获取许可,如果没有可用许可则返回false,不会阻塞
if (semaphore.tryAcquire()){
semaphore.release();
}

在线程之间发送信号

如果你将一个信号量用于在两个线程之间传送信号,通常你应该用一个线程调用 acquire() 方法,而另一个线程调用 release() 方法。
如果没有可用的许可,acquire() 调用将会阻塞,直到一个许可被另一个线程释放出来。同理,如果无法往信号量释放更多许可时,一个 release() 调用也会阻塞。
通过这个可以对多个线程进行协调。比如,如果线程 1 将一个对象插入到了一个共享列表(list)之后之后调用了 acquire(),而线程 2 则在从该列表中获取一个对象之前调用了 release(),这时你其实已经创建了一个阻塞队列。信号量中可用的许可的数量也就等同于该阻塞队列能够持有的元素个数。

更多方法

java.util.concurrent.Semaphore 类还有很多方法,比如:
availablePermits()
acquireUninterruptibly()
drainPermits()
hasQueuedThreads()
getQueuedThreads()
等等

分享到: