JUC系列(四)-深入讲解工具类之Semaphore

173 阅读3分钟

携手创作,共同成长!这是我参与「掘金日新计划 · 8 月更文挑战」的第25天,点击查看活动详情

简介

Semaphore译为信号量,它是一种线程同步工具,主要用于多个线程对共享资源进行并行操作的一种工具类。它代表了一种许可的概念,是否允许多线程对同一资源进行操作的许可,使用Semaphore 可以控制并发访问资源的线程个数。

基本工作流程

图片.png

说明: 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();
       }
    }
}

输出结果:

图片.png

源码分析

acquire

图片.png

从源码来看acquire调用的是sync中的acquireSharedInterruptibly方法。sync是继承于 AbstractQueuedSynchronizer的一个变量用来保证线程的安全性,通过结果来判断是否调用doAcquireSharedInterruptibly,而tryAcquireShared具体实现如下:

图片.png

tryAcquireShared的实现分为公平锁和非公平锁,那么具体实现为那个呢?我们需要从构造方法来进行确认

图片.png

从源码上来看,默认采用的时公平锁,只有当fair的值为true时才会采用非公平锁,那么公平锁和非公平锁的区别在于,公平锁能保证线程获得许可的顺序,公平锁中tryAcquireShared具体实现如下:

公平锁tryAcquireShared

图片.png

说明:从源码分析调用的是Semaphore中的Sync的nonfairTryAcquireShared方法,具体实现如下:

图片.png

说明:NonfairSync是不会管当前等待队列中是否有排队许可的,它会直接判断信号许可量和CAS方法的可行性

非公平锁tryAcquireShared

非公平锁和公平锁对于tryAcquireShared是有区别的,具体实现如下:

图片.png

说明:FairSync会判断是否有许可进行排队,如果有的话就直接获取失败,否则调用CAS方法。

doAcquireSharedInterruptibly具体实现

图片.png

release

图片.png

说明:release的方法调用的是sync中的releaseShared,而releaseShared具体实现如下:

图片.png

说明:releaseShared通过调用tryReleaseShared的结果来判断是否调用doReleaseShared方法,而tryReleaseShared具体实现如下:

图片.png

说明:尝试释放资源,成功则返回true,失败则返回false。而doReleaseShared的实现如下:

图片.png

总结

本文对于JUC的Semaphore进行了详细的讲解,如有疑问请随时反馈。