Java多线程 之 Semaphore源码分析

​目录

               1、背景介绍

               2、源码分析


一、背景介绍

信号量(Semaphore),也被称为信号量,是在多线程环境下使用的一种工具类, 它负责协调各个线程, 以保证它们能够以一定数量地、正确、合理的使用公共资源。

每一个线程获取到一个计数信号量后,计数信号量就会减1,知道计数信号量为0,表示计数信号量已经不可用了,在许可可用前会阻塞每一个 acquire()。。拿到信号量的线程可以进入代码,否则就等待。通过acquire()和release()获取和释放访问许可。

二、源码分析

Semaphore的实现其实也并不复杂。它的源码当中可以发现其核心功能是使用AQS进行实现的。

首先我们看其构造方法。

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

sync和NonfairSync应该已经很熟悉了,如果了解AQS框架的话。

private final Sync sync;
​
    /**
     * Synchronization implementation for semaphore.  Uses AQS state
     * to represent permits. Subclassed into fair and nonfair
     * versions.
     */
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;
​
        Sync(int permits) {
            setState(permits);
        }
​
        final int getPermits() {
            return getState();
        }
​
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
​
        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;
            }
        }
​
        final void reducePermits(int reductions) {
            for (;;) {
                int current = getState();
                int next = current - reductions;
                if (next > current) // underflow
                    throw new Error("Permit count underflow");
                if (compareAndSetState(current, next))
                    return;
            }
        }
​
        final int drainPermits() {
            for (;;) {
                int current = getState();
                if (current == 0 || compareAndSetState(current, 0))
                    return current;
            }
        }
    }
​
    /**
     * NonFair version
     */
    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -2694183684443567898L;
​
        NonfairSync(int permits) {
            super(permits);
        }
​
        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
    }
​
    /**
     * Fair version
     */
    static final class FairSync extends Sync {
        private static final long serialVersionUID = 2014338818796000944L;
​
        FairSync(int permits) {
            super(permits);
        }
​
        protected int tryAcquireShared(int acquires) {
            for (;;) {
                if (hasQueuedPredecessors())
                    return -1;
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
    }

以上就是基于AQS的实现。至于AQS这里不做重点讲解。下面我们来看本例中使用到的一些方法(acquire和release),对其源码进行分析。

 // 获取许可证
   semaphore.acquire()
​
   public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    } 
​
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }  

这是一种在共享模式下、响应中断地获取共享资源方式。它会获取指定量的资源,获取成功则直接返回,获取失败则进入等待队列,直到获取到资源为止,整个过程响应中断。

首先Thread.interrupted判断线程是否被中断,如果中断则直接抛出异常结束。tryAcquireShared依然是AQS的顶层接口,需要实现者自己去实现,Semaphore的实现逻辑如下。

 protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                int remaining = available - acquires;
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }

首先使用getState获取available可用资源,然后available可用资源数量减去acquires要申请的资源数量,如果返回为负,表示剩余可用资源少于请求占用资源数量,否则返回剩余可用资源数量或者全部资源已经都被占用(remaining为0)。

然后doAcquireSharedInterruptibly方法表示在剩余可用资源少于请求占用资源数量的情况下让线程进行等待资源被释放。

/**
     * Acquires in shared interruptible mode.
     * @param arg the acquire argument
     */
    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException 
        //添加到队列尾部
        final Node node 
        //标志是否获取成功
        boolean failed = true;
        try {
            for (;;) {
                //获取前驱结点
                final Node p = node.predecessor();
                if (p == head) {
                    //如果前驱结点为头结点,尝试去获取
                    int r = tryAcquireShared(arg);
                    if (r >= 0) {
                        //如果获取成功,去唤醒后继结点
                        setHeadAndPropagate(node, r);
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                //状态检查是否k可以park进行park操作
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            //如果fail为true,取消获取操作
            if (failed)
                cancelAcquire(node);
        }
    }

在共享可中断模式下进行等待资源被释放,这段代码执行逻辑注释已经标明,最终执行park操作之后线程就进入阻塞状态了。

接下来看第二个方法release的使用原理。

public void release() {
        sync.releaseShared(1);
    }
​
    public final boolean releaseShared(int arg) {
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

Semaphore直接调用sync的releaseShared方法,默认参数arg为1。

tryReleaseShared尝试去释放共享资源,这个具体释放逻辑需要实现者根据需要去实现。

  protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }
​
       //Semaphore的实现方式
       protected final boolean tryReleaseShared(int releases) {
            for (;;) {
                int current = getState();
                int next = current + releases;
                if (next < current) 
                    // 数量溢出
                    throw new Error("Maximum permit count exceeded");
                if (compareAndSetState(current, next))
                    return true;
            }
        }

Semaphore的实现方式并不复杂,getState获取current然后和释放数量releases进行相加,然后对releases进行合法性校验,接着CAS自旋的方式进行赋值state同步变量。如果成功返回true就可以唤醒后续的线程了。

doReleaseShared主要用于唤醒后继线程。

private void doReleaseShared() {
        /*
         * Ensure that a release propagates, even if there are other
         * in-progress acquires/releases.  This proceeds in the usual
         * way of trying to unparkSuccessor of head if it needs
         * signal. But if it does not, status is set to PROPAGATE to
         * ensure that upon release, propagation continues.
         * Additionally, we must loop in case a new node is added
         * while we are doing this. Also, unlike other uses of
         * unparkSuccessor, we need to know if CAS to reset status
         * fails, if so rechecking.
         */
        for (;;) {
            Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    //如果赋值失败就循环执行  
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;  
                    //唤醒后继              
                    unparkSuccessor(h);
                }
                else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

Node.SIGNAL表示结点Node的状态:

需要补加一点的是共享模式下的releaseShared()拥有资源的线程在释放掉部分资源时就可以唤醒后继等待结点。这一点是和独占模式下释放资源不同的一点。

至此,Semaphore在本例中的应用源码分析就分析完了,如果理解AQS框架下共享、独占模式下获取、释放共享资源原理的话对于本例中的执行原理应该还是很容易的。

 

更多内容持续更新中,感兴趣的朋友请移步至个人公众号,谢谢支持😜😜......

公众号:wenyixicodedog

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值