携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情
简介
Semaphore译为信号量,它是一种线程同步工具,主要用于多个线程对共享资源进行并行操作的一种工具类。它代表了一种许可的概念,是否允许多线程对同一资源进行操作的许可,使用Semaphore 可以控制并发访问资源的线程个数。
基本工作流程
说明: Semaphore有一个初始容量,这个初始容量就是Semaphore所能够允许的信号量。在调用Semaphore中的 acquire方法后,Semaphore 的容量减1,相对的在调用release方法后,Semaphore 的容量加1,在这个过程中,计数器一直在监控Semaphore 数量的变化,等到流量超过Semaphore的容量后,多余的流量就会放入等待队列中进行排队等待。等到Semaphore的容量允许后,方可重新进入。
使用场景
Semaphore的使用场景主要用于流量控制,比如数据库连接,同时使用的数据库连接会有数量限制,数据库连接不能超过一定的数量,当连接到达了限制数量后,后面的线程只能排队等。
案例说明
过年了大家都需要抢票,但是抢票的人数比放票数量多很多,所以只能有部分人员抢到票,除非是抢到票的没有付钱,释放的票才能重新去抢。
public class SemaphoreTest
{
private static Logger logger =LoggerFactory.getLogger(SemaphoreTest.class);
public static void main(String[] args)
{
//总共才3张票
Semaphore semaphore = new Semaphore(3);
//10个人去抢票
for (int i = 1; i <= 10; i++) {
new Thread(() -> {
try {
//
semaphore.acquire();
logger.info(Thread.currentThread().getName() + "我抢到票了");
TimeUnit.SECONDS.sleep(2);
logger.info(Thread.currentThread().getName() + "没有付钱,退票了");
} catch (InterruptedException e) {
logger.error("get resource error",e);
} finally {
//释放
semaphore.release();
}
}).start();
}
}
}
输出结果:
源码分析
acquire
从源码来看acquire调用的是sync中的acquireSharedInterruptibly方法。sync是继承于 AbstractQueuedSynchronizer的一个变量用来保证线程的安全性,通过结果来判断是否调用doAcquireSharedInterruptibly,而tryAcquireShared具体实现如下:
tryAcquireShared的实现分为公平锁和非公平锁,那么具体实现为那个呢?我们需要从构造方法来进行确认
从源码上来看,默认采用的时公平锁,只有当fair的值为true时才会采用非公平锁,那么公平锁和非公平锁的区别在于,公平锁能保证线程获得许可的顺序,公平锁中tryAcquireShared具体实现如下:
公平锁tryAcquireShared
说明:从源码分析调用的是Semaphore中的Sync的nonfairTryAcquireShared方法,具体实现如下:
说明:NonfairSync是不会管当前等待队列中是否有排队许可的,它会直接判断信号许可量和CAS方法的可行性
非公平锁tryAcquireShared
非公平锁和公平锁对于tryAcquireShared是有区别的,具体实现如下:
说明:FairSync会判断是否有许可进行排队,如果有的话就直接获取失败,否则调用CAS方法。
doAcquireSharedInterruptibly具体实现
release
说明:release的方法调用的是sync中的releaseShared,而releaseShared具体实现如下:
说明:releaseShared通过调用tryReleaseShared的结果来判断是否调用doReleaseShared方法,而tryReleaseShared具体实现如下:
说明:尝试释放资源,成功则返回true,失败则返回false。而doReleaseShared的实现如下:
总结
本文对于JUC的Semaphore进行了详细的讲解,如有疑问请随时反馈。