前言
上节我们介绍了Java中的并发工具类CountDownLatch和Cyclicbarrier,今天我们再来说说另外两个并发工具类:Semaphore(信号量)和Exchanger(交换者),首先我们先来说说信号量这个东西,结合我们今天所讲的,我们了解下,这个东西到底有啥用,话不多说,让我们开启今天的并发之旅吧。
什么是信号量?
信号量是用来控制同时访问特定资源的线程数量,通过协调各个线程,以保证合理的使用公共资源。
这样说可能大家不是很理解,那我就举个生活中常见的场景,结合场景描述下Semaphore这个东西:
我们大家都去过大型商场,都知道商场都有自己的停车场,假设一个停车场允许的最大停车数量是200,当我们到达停车场时,会发现上面显示着剩余车位,如果剩余车位0,那么栏杆会扫描完车牌放行,如果剩余车位=0,则说明已没有能使用的车位,此时就不放行,所以进入商场的车都需要排队等候,当里面有离开停车场,离开n辆就会放行n辆,这里用来控制是否放行的剩余车位数就可以理解为是一个信号量指示,成功扫描,或者拿到停车票的车子就能进入,他用来控制当前进入商场的车是否能放行。
如何使用信号量?
使用信号量我们首先要创建一个信号量
public Semaphore(int permits) {
sync = new NonfairSync(permits);
}
然后就可以调用Semaphore的相关方法,主要有如下几个方法:
// 获取一个信号量许可证
public void acquire() throws InterruptedException {
sync.acquireSharedInterruptibly(1);
}
// 释放获取的信号量许可证,一次释放一个
public void release() {
sync.releaseShared(1);
}
// 返回信号量中可以使用的许可证数
public int availablePermits() {
return sync.getPermits();
}
// 返回正在等待获取许可证的线程数
public final int getQueueLength() {
return sync.getQueueLength();
}
// 返回Boolean值是否存在线程正在等待获取许可证
public final boolean hasQueuedThreads() {
return sync.hasQueuedThreads();
}
// 返回所有正在等待获取许可证的线程集合 AQS操作
protected CollectionThread getQueuedThreads() {
return sync.getQueuedThreads();
}
// AQS中的源码
public final CollectionThread getQueuedThreads() {
ArrayListThread list = new ArrayListThread();
for (Node p = tail; p != null; p = p.prev) {
Thread t = p.thread;
if (t != null)
list.add(t);
}
return list;
}
OK,下面我们看看如何使用信号量,我们使用Semaphore来模拟一个停车场停车的场景:
public class SemaphoreDemo {
public static void main(String[] args) {
// 线程池
ExecutorService exec = Executors.newCachedThreadPool();
// 假设停车场一共有停车位5个
final Semaphore semp = new Semaphore(5);
// 模拟10个车子进入停车场
for (int index = 10000; index 10011; index++) {
final int NO = index;
Runnable run = new Runnable() {
public void run() {
try {
// 获取许可
semp.acquire();
System.out.println("获得停车证进场车牌号:沪A" + NO);
// 模拟一个车子在停车场停留时间
Thread.sleep((long) (Math.random() * 10000));
// 访问完后,释放,离开停车场
semp.release();
System.out.println("沪A" + NO+"离开停车场");
} catch (InterruptedException e) {
}
}
};
exec.execute(run);
}
try {
Thread.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
// 退出线程池
exec.shutdown();
}
}
执行结果:
其实Semaphore也可以理解为是实现了等待唤醒功能,你也可以使用wait/notifyAll来实现上述模拟,感兴趣的小伙伴可以自己试试。
原文始发于微信公众号(Justin的后端书架):