信号量(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来实现一个有指定容量的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(); } }