Semaphore源码解析

前言

AQS源码解析一中,我们说到了AQS对锁资源获取的过程进行了抽象,帮我们实现了线程如何被挂起、如何被唤醒等操作,分析了独占模式和共享模式底层是如何实现的,今天我们来看看Semaphore(信号量)底层是怎么控制资源获取和释放的,这样可以进一步加深我们对AQS的理解。

Semaphore

内部使用共享锁实现
有公平和非公平的两种实现
以许可证为共享资源控制使用共享资源的线程数
线程抢夺到指定个数的许可证后 可执行
用完然后释放掉 阻塞的线程会抢夺许可证 抢到许可证后可执行

先来看看Semaphore的重要构造

    /**
     * @param permits 请求获取的许可证数量
     * @param fair true 公平 false 非公平
     */
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }

可以看到,内部是有公平和非公平的两种实现方式的

非公平模式下的获取与释放


非公平模式下的获取许可证

    /**
     * 	获取一个许可证 可中断
     */
    public void acquire() throws InterruptedException {
    	//调用的是AQS中的模板方法
        sync.acquireSharedInterruptibly(1);
    }

    /**
     * 	获取共享锁  可中断 ASQ中的方法
     */
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
    	//当前线程被标记为中断的 直接抛出异常
        if (Thread.interrupted())
            throw new InterruptedException();
        //尝试获取共享锁 子类实现小于0
        if (tryAcquireShared(arg) < 0)
        	//获取共享锁 AQS实现
            doAcquireSharedInterruptibly(arg);
    }

可以看到AQS的模板方法,这里是可中断的

1.如果线程被标记中断的就自己抛出异常
2.调用子类的tryAcquireShared返回小于0 调用doAcquireSharedInterruptibly,否则就是获取成功了

来看看Semaphore非公平模式对tryAcquireShared方法的实现
在这里插入图片描述
非公平的情况下,谁来都先可以尝试获取一下

doAcquireSharedInterruptibly,这在AQS中有详细说到,线程所在的节点前驱如果是头节点,会再调用Semaphore下的tryAcquireShared尝试获取共享锁,如果获取成功之后返回的可用许可证大于0的话,会doReleaseShared唤醒等待共享锁的线程,获取失败的话线程很有可能会被挂起,然后直到被唤醒。

非公平模式下的释放许可证

    /**
     * 	释放一个许可证
     */
    public void release() {
    	//调用的是AQS中的模板方法
        sync.releaseShared(1);
    }
    /**
     * 	释放共享锁	AQS中的方法
     */
    public final boolean releaseShared(int arg) {
    	//尝试释放共享锁成功 子类实现
        if (tryReleaseShared(arg)) {
            //唤醒后继阻塞节点
        	doReleaseShared();
            return true;
        }
        return false;
    }

1.非公平模式的tryReleaseShared如果释放成功,就会调用doReleaseShared唤醒等待共享锁的线程

来看看Semaphore非公平模式对tryReleaseShared方法的实现
在这里插入图片描述
CAS+自旋保证释放成功

公平模式下的获取与释放


公平模式下的获取

AQS部分都和非公平锁一样,不同点就是在于tryAcquireShared,获取资源的具体策略上的不同。

来看看Semaphore公平模式对tryAcquireShared方法的实现
在这里插入图片描述
与非公平对比可以发现,多了一步**hasQueuedPredecessors()**阻塞队列中已经有线程在排队了直接返回-1(也就是前面有人了,按先到先得公平的方式,已经轮不到你了) 获取失败的,其它与非公平都一样。


公平模式下的释放许可证

        /**
         *	尝试释放许可证
         *	@param releases 准备释放的许可证个数
         */
        protected final boolean tryReleaseShared(int releases) {
            for (;;) {//死循环
            	//获取当前可用的许可证数量
                int current = getState();
                int next = current + releases;
                if (next < current) 
                    throw new Error("Maximum permit count exceeded");
                //cas设置许可证的数量 成功返回true
                if (compareAndSetState(current, next))
                    return true;
            }
        }

与非公平释放许可的过程一样。

总结

Semaphore使用共享锁来控制资源的获取,从而控制共享资源的获取。内部使用公平与非公平两种方式,公平:效率高,但是可能会出现长时间等待的线程,非公平:先到先得,后来要排队,可以保证不会有线程长时间等待得不到执行。关于Semaphore完整的源码解析点击这里

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值