信号量(Semaphore)又称为信号量、旗语,它以一个整数变数,提供信号,以确保在并行计算环境中,不同进程在访问共享资源时,不会发生冲突。是一种不需要使用忙碌等待(busy waiting)的一种方法。
信号量的概念是由荷兰计算机科学家艾兹格·迪杰斯特拉(Edsger W. Dijkstra)发明的,广泛的应用于不同的操作系统中。在系统中,给予每一个进程一个信号量,代表每个进程目前的状态,未得到控制权的进程会在特定地方被强迫停下来,等待可以继续进行的信号到来。如果信号量是一个任意的整数,通常被称为计数信号量(Counting semaphore),或一般信号量(general semaphore);如果信号量只有二进制的0或1,称为二进制信号量(binary semaphore)。在linux系中,二进制信号量(binary semaphore)又称Mutex。
java中实现的是计数信号量,用来控制同时访问某个特定资源的操作数量,或者同时执行某个指定操作的数量。计数信号量还可以用来实现某种资源池,或者对容器施加边界。
java中实现计数信号量的类为java.util.concurrent.Semaphore,是在1.5中引入的。Semaphore中管理着一组许可,许可的初始数量可以通过构造方法来指定。在执行操作时需要先获得许可(acquire),并在使用完后释放许可(release)。如果当前没有许可,那么acquire将阻塞下到有许可可用,或者直到被中断,或者操作超时。
使用方式
使用方式可以像下面这样:
package com.mikan.thread;
import java.util.concurrent.Semaphore;
/**
* @author Mikan
* @date 2015-08-29 18:40
*/
public class SemaphoreTest {
private Semaphore semaphore;
public SemaphoreTest(int permits) {
if (permits <= 0) {
throw new IllegalArgumentException("permits must be greater than 0");
}
semaphore = new Semaphore(permits);
}
public void operation() throws InterruptedException {
semaphore.acquire();
try {
// do something......
}
finally {
semaphore.release();
}
}
}
Semaphore的实现是基于java.util.concurrent.locks.AbstractQueuedSynchronizer(AQS)基类,JDK中许多同步类都是基于它来实现的,像ReentrantLock、CountDownLatch、ReentrantReadWriteLock等。Semaphore的实现使用AQS的状态来保存许可数量,它实现了公平和非公平两种策略,默认下是非公平策略,可以在创建Semaphore时指定公平策略。当许可为1时,可以当作互斥锁来使用。
使用Semaphore来实现有界容器
比如这里使用Semaphore来实现一个有指定容量的List:
package com.mikan.thread;
import java.util.Collections;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.Semaphore;
/**
* @author Mikan
* @date 2015-08-29 19:11
*/
public class BoundedList<T> {
private final List<T> list;
private final Semaphore semaphore;
public BoundedList(int bound) {
list = Collections.synchronizedList(new LinkedList<T>());
semaphore = new Semaphore(bound);
}
public boolean add(T obj) throws InterruptedException {
semaphore.acquire();
boolean addedFlag = false;
try {
addedFlag = list.add(obj);
}
finally {
if (!addedFlag) {
semaphore.release();
}
}
return addedFlag;
}
public boolean remove(Object obj) {
boolean removedFlag = list.remove(obj);
if (removedFlag) {
semaphore.release();
}
return removedFlag;
}
// 其他操作委托给底层的List,这里只列举出一个方法
public T get(int index) {
return list.get(index);
}
// 其他方法……
}
使用信号量来控制同时执行的线程数量
package com.mikan.thread;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.Semaphore;
/**
* @author Mikan
* @date 2015-08-29 19:47
*/
public class BoundedThreadExecution {
public static void main(String[] args) {
final Semaphore semaphore = new Semaphore(5);
final CountDownLatch gate = new CountDownLatch(1);
int maxThreads = 100;
for (int i = 0; i < maxThreads; i++) {
final int index = i;
new Thread(new Runnable() {
@Override
public void run() {
try {
gate.await();
semaphore.acquire();
System.out.println(Thread.currentThread().getName() + "-->" + index + "--available permits=" + semaphore.availablePermits());
} catch (InterruptedException e) {
e.printStackTrace();
Thread.currentThread().interrupt();
}
finally {
// 不管当前线程是否正常结束,都释放许可
semaphore.release();
}
}
}).start();
}
// 使用闭锁来实现所有线程同时开始执行
gate.countDown();
}
}
自己实现一个计数信号量
package com.mikan.thread;
/**
* @author Mikan
* @date 2015-08-29 19:19
*/
public class CountingSemaphore {
private final int bound;
private int permits = 0;
public CountingSemaphore(int permits) {
if (permits <= 0) {
throw new IllegalArgumentException("permits must be greater than 0");
}
this.bound = permits;
}
public synchronized void acquired() throws InterruptedException {
// 当许可达到上限时,则阻塞
while (permits == bound) {
wait();
}
permits++;
}
public synchronized void release() {
permits--;
// 释放了许可,通知等待的线程
notifyAll();
}
}