AQS之Semaphore源码分析

1: Semaphore介绍

信号量提供了用于控制资源同时被访问的个数,也就是它会维护一个许可证,访问资源之前需要申请许可证,申请许可证成功后才可以进行访问,如果申请访问资源获取的了许可证,则可以进行资源访问,同时颁发许可证中心的许可证会进行增加,等到访问资源的线程释放资源后,许可证使用情况会进行减少,可用于访问限流等各类限制数量访问等业务。

2: Semaphore加锁逻辑

在这里插入图片描述

2.1:acquireSharedInterruptibly(int arg),开始获取资源

   public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        //判断线程是否中断
        if (Thread.interrupted())
            throw new InterruptedException();
        //判断是否存在空余资源,如果小于零,进行线程中断,添加到队列
        if (tryAcquireShared(arg) < 0)
            //进行中断
            doAcquireSharedInterruptibly(arg);
    }

2.2: tryAcquireShared->nonfairTryAcquireShared(int acquires),获取剩余资源数

 final int nonfairTryAcquireShared(int acquires) {
        for (;;) {
            //获取当前可用的资源数
            int available = getState();
            //默认占用的资源数-可用资源数
            int remaining = available - acquires;
            if (remaining < 0 ||
                    //进行cas操作
                    compareAndSetState(available, remaining))
                //返回剩余资源数
                return remaining;
        }
    }

2.3: doAcquireSharedInterruptibly(int arg),进行线程中断

    private void doAcquireSharedInterruptibly(int arg)
            throws InterruptedException {
        //将当前节点添加到队列中
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                //获取上一个节点
                final Node p = node.predecessor();
                //头节点是否等于上一个节点
                if (p == head) {
                    //获取当前剩余资源数
                    int r = tryAcquireShared(arg);
                    //如果存在剩余资源数
                    if (r >= 0) {
                        //将当前链表的上一个节点设置为空
                        setHeadAndPropagate(node, r);
                        //将上一个节点的下一个节点连接设置为空,进行GC去除
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                //如果没有剩余资源,进行链表状态修改,并且线程中断
                if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

2.3.1: addWaiter(Node mode),生成双向链表

   private Node addWaiter(Node mode) {
        Node node = new Node(Thread.currentThread(), mode);
        //获取尾节点
        Node pred = tail;
        //如果尾节点不为空
        if (pred != null) {
            //将当前线程的头节点连接到链表尾节点
            node.prev = pred;
            //cas链表的尾节点添加当前线程,形成双向绑定
            if (compareAndSetTail(pred, node)) {
                //链表的尾节点添加当前线程,形成双向绑定
                pred.next = node;
                return node;
            }
        }
        //如果尾节点为空,进行初始化
        enq(node);
        return node;
    }
2.3.1.1:enq(final Node node) 添加尾节点
 private Node enq(final Node node) {
        //死循环
        for (;;) {
            //获取尾节点
            Node t = tail;
            //如果为空
            if (t == null) { // Must initialize
                //当前节点绑定到尾节点,再次循环
                if (compareAndSetHead(new Node()))
                    tail = head;
            } else {
                //尾节点赋值给当前节点的头节点
                node.prev = t;
                //设置尾节点的下一个节点为当前节点
                if (compareAndSetTail(t, node)) {
                    t.next = node;
                    return t;
                }
            }
        }
    }

2.3.2:doAcquireSharedInterruptibly(int arg),设置上个节点状态

    private void doAcquireSharedInterruptibly(int arg)
            throws InterruptedException {
        //将当前节点添加到队列中
        final Node node = addWaiter(Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                //获取上一个节点
                final Node p = node.predecessor();
                //头节点是否等于上一个节点
                if (p == head) {
                    //获取当前剩余资源数
                    int r = tryAcquireShared(arg);
                    //如果存在剩余资源数
                    if (r >= 0) {
                        //将当前链表的上一个节点设置为空
                        setHeadAndPropagate(node, r);
                        //将上一个节点的下一个节点连接设置为空,进行GC去除
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                //如果没有剩余资源,进行链表状态修改,并且线程中断
                if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

3: Semaphore解锁逻辑

在这里插入图片描述

3.1:tryReleaseShared(int releases),释放资源

    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;
        }
    }

3.2:doReleaseShared(),进行资源解锁

    private void doReleaseShared() {
        for (;;) {
            //获取当前节点的头节点
            Node h = head;
            if (h != null && h != tail) {
                //获取上一个节点状态
                int ws = h.waitStatus;
                if (ws == Node.SIGNAL) {
                    //如果修改失败跳过这次循环
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    //成功进行解锁处理
                    unparkSuccessor(h);
                }
                //如果状态值不为Node.SIGNAL,修改当前状态Node.PROPAGATE
                else if (ws == 0 &&
                        !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

戴~

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值