java.util.concurrent.Semaphore提供了控制并发的工具类,关键方法是acquire()和release(),它提供一种基于许可的同步机制,初始化一定的许可数量,同步参与者先获取许可,再执行操作,然后释放许可。假如将许可数设置为1,就是一种简单的互斥锁的实现,即同一时间只有一个线程获取到许可。见下面的代码示例:
public class TestSemaphore {
public static void main(String[] args) {
final Semaphore s = new Semaphore(1);
for (int i=0; i<10; i++) {
new Thread("worker" + i) {
public void run() {
try {
s.acquire();
Thread curT = Thread.currentThread();
System.out.println(curT + ": I am tired, sleeping~");
Thread.sleep(Math.round(Math.random() * 3000));
System.out.println(curT + ": sleep over~");
} catch (InterruptedException e) {
e.printStackTrace();
}
s.release();
}
}.start();
}
}
}
Semaphore可以控制同一时间的并发数,于是自然可以想到另一个常用的使用场景--资源池,如下面的代码示例,实现任意资源的池化。
class MyPool {
Object[] items;
int poolSize;
Semaphore available;
boolean[] used;
public MyPool(Object[] poolRes) {
if (null == poolRes) {
poolRes = new Object[0];
}
this.items = poolRes;
this.poolSize = poolRes.length;
this.available = new Semaphore(poolSize, true);
this.used = new boolean[poolSize];
}
public Object acquire() throws InterruptedException {
available.acquire();
return getNextAvailableItem();
}
public void release(Object x) {
if (markAsUnused(x)) {
available.release();
}
}
private synchronized Object getNextAvailableItem() {
for (int i=0; i<poolSize; ++i) {
if (!used[i]) {
used[i] = true;
return items[i];
}
}
return null; // not reached
}
private synchronized boolean markAsUnused(Object item) {
for (int i=0; i<poolSize; ++i) {
if (item == items[i]) {
if (used[i]) {
used[i] = false;
return true;
}
return false;
}
}
return false;
}
}
和前面几篇文章一起,分别介绍了CountDownLatch/CyclicBarrier/Exchanger/Semaphore/Phaser,下面对各自的常用使用场景做个小小的总结:
1、Semaphore
控制并发数/互斥锁、资源池
2、Exchanger
协作成对线程交互数据
3、CountDownLatch
等待N个条件到达,等待一起出发。
4、CyclicBarrier
阶段协同器(各阶段参与者数量固定)
5、Phaser
阶段协同器(各阶段参与者数量可变)