Semaphore源码解析

时间:2022-07-23
本文章向大家介绍Semaphore源码解析,主要内容包括其使用实例、应用技巧、基本知识点总结和需要注意事项,具有一定的参考价值,需要的朋友可以参考一下。

Semaphore(信号量)是控制访问线程的基于AQS的并发工具。也就是说使用Semaphore能够控制访问资源的并发量。比如在限流这块。

按照限流的逻辑,所以在使用的时候,需要进行设置最大的并发线程,按照我们之前对countDloadLatch的理解。通过设定总量,然后在条件满足之后进行下一工作。

semaphore则是通过设定最大的并发线程,在当前线程消耗的指定的掉总量之后。总量的剩余量与当初设定的总量进行对比,如果小于总量就排队,如果大于总量就进行阻塞。如果超过超时时间就剔除。

semaphore的大致逻辑就是这样。其中使用了AQS做基础的保证。

public class MySemaphore {


    public static Semaphore semaphore=new Semaphore(2);


    static class task implements Runnable{




        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName());
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                semaphore.release();
            }
        }
    }




    public static void main(String[] args) {
        ExecutorService executorService= Executors.newCachedThreadPool();
        for (int i=0;i<10;i++){
            executorService.submit(new task());
        }
        executorService.shutdown();


    }
}

我们可以修改Semaphore的值,然后观察结果,能够很明显的观察到这种并发控制的效果。

下面我们从源码角度看一下他是如何实现的。

从图中可以看出,Seamaphore的结构和其他的并发工具一样,都有公平锁和非公平锁,一版这种情况都是和AQS关系很大,然后看到很多之前我们看到的方法。

下面我们从初始化方法和上述demo的层面上进行深入。

    public Semaphore(int permits) {
        sync = new NonfairSync(permits);
    }

在初始化的时候,默认采用的是非公平锁。

    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

或者自行决定是公平还是非公平锁

    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

当我们调用acquire()方法的时候,发现调用的是acquireSharedInterruptibly,我们跟踪一下。

doAcquireShared这个方法我们之前分析过,就是要从AQS链表中从后向前的寻找。剔除超时的,把能激活的线程向前移动,然后等待之前的额线程执行完毕之后抢夺head节点。

那么这块的意思就是说要先获取state的数量,然后看state减去申请的数量(这里是1),是否还有剩余,如果有剩余说明可以加入到队列。如果state没有剩余,那么就进行自旋,然后一直尝试往链表中添加。

按照这种逻辑,那么释放的时候,就应该是是对state的值进行相加。那么我们看看源码是如何实现的。

        protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) // overflow
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }

果然,Semaphore就是对state的值进行加法操作。这样保证信号量的值的统一。

好了关于Semaphore的源码解析就到这里了。

总结:

Semaphore使用AQS进行信号量的实现。通过对state值得维护来实现信号量模式,在实际应用中,我们可以采用Semaphore来做系统的限流业务。