Concurrent -- 07 -- Semaphore源码解析

原文链接:Concurrent – 07 – Semaphore源码解析


相关文章:


Semaphore,即信号量,是一个同步工具类,用于控制某个资源可被同时访问的线程个数


一、内部类解析

  • Sync

    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;
    
        // Sync 构造方法,初始化许可数目
        Sync(int permits) {
            setState(permits);
        }
    
        // 获取当前许可数目
        final int getPermits() {
            return getState();
        }
    
        // 非公平模式尝试获取许可
        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                // 获取当前许可数目
                int available = getState();
                // 减去这次需要获取的许可数目
                int remaining = available - acquires;
                /*
                 * 如果剩余许可数目小于 0,则直接返回;
                 *
                 * 如果剩余许可数目大于等于 0,则使用 CAS 操作将
                 * 当前许可数目由 available 更新为 remaining,
                 * 并返回剩余许可数目
                 */
                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");
                /*
                 * 如果未溢出,则使用 CAS 操作将
                 * 当前许可数目由 current 更新为 next,
                 * 并返回 true
                 */
                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");
                /*
                 * 如果未溢出,则使用 CAS 操作将
                 * 当前许可数目由 current 更新为 next,
                 * 并直接返回
                 */
                if (compareAndSetState(current, next))
                    return;
            }
        }
    
        // 销毁许可
        final int drainPermits() {
            for (;;) {
                // 获取当前许可数目
                int current = getState();
                /*
                 * 如果当前许可数目为 0,则直接返回;
                 *
                 * 如果当前许可数目不为 0,则使用 CAS 操作将
                 * 当前许可数目由 current 更新为 0,
                 * 并返回当前许可数目
                 */
                if (current == 0 || compareAndSetState(current, 0))
                    return current;
            }
        }
    }
    
    • Sync 是 Semaphore 的内部类,用于帮助 Semaphore 进行同步控制,其继承自 AQS,并使用 AQS 的同步状态 (state) 来表示许可数目
  • NonfairSync

    static final class NonfairSync extends Sync {
        private static final long serialVersionUID = -2694183684443567898L;
    
        // NonfairSync 构造方法,调用父类的构造方法来初始化许可数目
        NonfairSync (int permits) {
            super(permits);
        }
    
        // 尝试获取许可,调用父类的 nonfairTryAcquireShared(int acquires) 方法来获取许可
        protected int tryAcquireShared(int acquires) {
            return nonfairTryAcquireShared(acquires);
        }
    }
    
    • 非公平模式,NonfairSync 继承自 Sync

    • 在使用过程中,会通过调用 Sync 的 nonfairTryAcquireShared(int acquires) 方法来获取许可

  • FairSync

    static final class FairSync extends Sync {
        private static final long serialVersionUID = 2014338818796000944L;
    
        // FairSync 构造方法,调用父类的构造方法来初始化许可数目
        FairSync(int permits) {
            super(permits);
        }
    
        // 尝试获取许可
        protected int tryAcquireShared(int acquires) {
            for (;;) {
                // 判断等待队列中有没有比当前线程等待时间更长的线程 (即优先级更高)
                if (hasQueuedPredecessors())
                    // 如果有,则返回 -1
                    return -1;
                // 如果没有,则继续执行
                // 获取当前许可数目
                int available = getState();
                // 减去这次需要获取的许可数目
                int remaining = available - acquires;
                /*
                 * 如果剩余许可数目小于 0,则直接返回;
                 *
                 * 如果剩余许可数目大于等于 0,则使用 CAS 操作将
                 * 当前许可数目由 available 更新为 remaining,
                 * 并返回剩余许可数目
                 */
                if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                    return remaining;
            }
        }
    }
    
    • 公平模式,FairSync 继承自 Sync

    • 在使用过程中,会先检测是否有比当前线程优先级更高的线程,如果有,则获取许可失败,进入 CLH 队列;如果没有,则获取许可成功,并使用 CAS 操作更新许可数目


二、字段解析

  • sync

    private final Sync sync;
    
    • sync 是内部类 Sync 的一个实例

三、构造方法解析

  • Semaphore(int permits)

    public Semaphore(int permits) {
        // 实例化 NonfairSync 对象 (非公平模式)
        sync = new NonfairSync(permits);
    }
    
  • Semaphore(int permits)

    public Semaphore(int permits, boolean fair) {
        /*
         * 根据 fair 来判断使用公平模式还是非公平模式
         * true
         *      实例化 FairSync 对象 (公平模式)
         * false
         *      实例化 NonfairSync 对象 (非公平模式)
         */
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }
    

四、方法解析

1、acquire() 相关方法
  • acquire()

    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }
    
    • 获取许可,阻塞当前线程,直到获取到许可或线程被中断为止
  • acquireSharedInterruptibly(int arg)

    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        // 如果线程被中断,则抛出异常
        if (Thread.interrupted())
            throw new InterruptedException();
        // 尝试获取共享锁
        if (tryAcquireShared(arg) < 0)
            // 在可中断模式下获取共享锁
            doAcquireSharedInterruptibly(arg);
    }
    
    • 获取共享锁,如果中断则中止
  • tryAcquireShared(int arg)

    protected int tryAcquireShared(int arg) {
        throw new UnsupportedOperationException();
    }
    
    • 尝试获取共享锁,该方法没有具体实现,交由子类实现,此处实际调用的是 Semaphore 内部类 NonfairSync 中的 tryAcquireShared(int acquires) 方法 (非公平模式) 或内部类 FairSync 中的 tryAcquireShared(int acquires) 方法 (公平模式)

    • 非公平模式,当前线程获取许可,并将许可数目减 1

    • 公平模式,如果没有优先级更高的线程,则当前线程获取许可,并将许可数目减 1;如果有优先级更高的线程,则阻塞当前线程,并将其加入 CLH 队列

  • doAcquireSharedInterruptibly(int arg)

    private void doAcquireSharedInterruptibly(int arg)
        throws InterruptedException {
        // 创建一个共享节点 node,将其加入到 CLH 队列尾部
        final Node node = addWaiter(Node.SHARED);
        // 标记是否成功获取资源
        boolean failed = true;
        try {
            for (;;) {
                // 获取 node 节点的前驱节点 p
                final Node p = node.predecessor();
                // 如果 p 节点为头部节点,则 node 节点会尝试去获取资源
                if (p == head) {
                    /*
                     * 此处再次调用了 Semaphore 
                     * 内部类 NonfairSync 中的 tryAcquireShared(int acquires) 方法 (非公平模式) 
                     * 或内部类 FairSync 中的 tryAcquireShared(int acquires) 方法 (公平模式)
                     */
                    int r = tryAcquireShared(arg);
                    // 如果剩余许可数目大于等于 0
                    if (r >= 0) {
                        // 将 node 节点设置为头部节点,并唤醒后继节点
                        setHeadAndPropagate(node, r);
                        /*
                         * 此时 CLH 队列的头部节点为 node,
                         * 原先的头部节点 p 已处理完资源出队,
                         * 因此将 p 节点的 next 引用设置为 null
                         * 方便 GC 对 P 节点进行回收
                         */
                        p.next = null; // help GC
                        failed = false;
                        return;
                    }
                }
                /*
                 * 如果 p 节点不为头部节点,则根据 p 节点
                 * 的状态来判断是否要阻塞 node 节点
                 */
                if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            // 如果未成功获取资源,则取消 node 节点获取锁的尝试
            if (failed)
                cancelAcquire(node);
        }
    }
    
    • 在可中断模式下获取共享锁
  • addWaiter(Node mode)

    private Node addWaiter(Node mode) {
        /*
         * 根据当前线程和给定模式,创建一个 Node 节点
         * model: SHARED(共享)、EXCLUSIVE(独占)
         */
        Node node = new Node(Thread.currentThread(), mode);
        // 将 pred 节点的引用指向尾部节点
        Node pred = tail;
        // 如果 pred 节点不为 null
        if (pred != null) {
            // 则将 pred 节点设置为 node 节点的前驱节点
            node.prev = pred;
            // 使用 CAS 操作将尾部节点由 pred 节点更新为 node 节点
            if (compareAndSetTail(pred, node)) {
                // 将 node 节点设置为 pred 节点的后继节点
                pred.next = node;
                return node;
            }
        }
        /*
         * 如果 pred 节点为 null,或上述 
         * compareAndSetTail(pred, node) 操作失败,
         * 则调用 enq(final Node node) 方法将 node
         * 节点加入到 CLH 队列尾部
         */
        enq(node);
        return node;
    }
    
    • 根据当前线程和给定模式,创建一个 Node 节点,将其加入到 CLH 队列尾部
  • enq(final Node node)

    private Node enq(final Node node) {
        for (;;) {
            // 将 t 节点的引用指向尾部节点
            Node t = tail;
            // 如果 t 节点为 null
            if (t == null) {
                /*
                 * 说明队列为空,则创建一个新的 Node
                 * 节点作为队列的头部节点和尾部节点
                 */
                if (compareAndSetHead(new Node()))
                    tail = head;
            // 如果 t 节点不为 null
            } else {
                // 说明队列不为空,则将 t 节点设置为当前节点的前驱节点
                node.prev = t;
                // 使用 CAS 操作将尾部节点由 t 节点更新为当前节点
                if (compareAndSetTail(t, node)) {
                    // 将当前节点设置为 t 节点的后继节点
                    t.next = node;
                    return t;
                }
            }
        }
    }
    
    • 将当前节点加入到 CLH 队列尾部
  • setHeadAndPropagate(Node node, int propagate)

    private void setHeadAndPropagate(Node node, int propagate) {
        // 将 h 节点的引用指向旧的头部节点
        Node h = head;
        // 将当前节点设置为新的头部节点
        setHead(node);
        
        /*
         * 此处 propagate 为 Semaphore 内部类 NonfairSync 中的 
         * tryAcquireShared(int acquires) 方法 (非公平模式) 的返回值,
         * 或内部类 FairSync 中的 tryAcquireShared(int acquires) 方法 (公平模式) 的返回值,
         * 即剩余许可数目,是决定是否传播的依据之一
         * 
         * 如果剩余许可数目大于 0,或旧头部节点为 null,
         * 或旧头部节点状态不为 CANCELLED(1) 或默认状态 (0),
         * 或新头部节点为 null,或新头部节点状态不为 CANCELLED(1) 或默认状态 (0)
         */
        if (propagate > 0 || h == null || h.waitStatus < 0 ||
            (h = head) == null || h.waitStatus < 0) {
            // 将 s 节点的引用指向当前节点的后继节点
            Node s = node.next;
            /*
             * 如果 s 节点为 null 或 s 节点为
             * 共享节点,则对后继节点进行唤醒传播
             */
            if (s == null || s.isShared())
                doReleaseShared();
        }
    }
    
    • 设置当前节点为头部节点,并根据 tryAcquireShared(int acquires) 方法的返回值以及节点状态来判断是否需要唤醒后继节点
  • doReleaseShared()

    private void doReleaseShared() {
        /*
         * 如果头部节点存在后继节点,且节点状态为 SIGNAL(-1),则唤醒后继节点;
         * 如果头部节点存在后继节点,且节点状态为默认状态 (0),
         * 则为了保证唤醒操作可以正确稳定地传播下去,需要设置头部节点状态为 
         * PROPAGATE(-3),这样的话,当获取锁的线程在执行 setHeadAndPropagate(Node node, int propagate) 
         * 方法时可以读取到头部节点的 PROPAGATE(-3) 状态,从而让获取锁的线程去唤醒后继节点
         */
        for (;;) {
            // 将 h 节点的引用指向头部节点
            Node h = head;
            /*
             * 如果 h 节点不为 null 且 h 节点
             * 不是尾部节点 (说明 h 节点存在后继节点)
             */
            if (h != null && h != tail) {
                // 获取 h 节点状态
                int ws = h.waitStatus;
                // 如果 h 节点状态为 SIGNAL(-1)
                if (ws == Node.SIGNAL) {
                    /* 使用 CAS 操作将 h 节点状态由 SIGNAL(-1)
                     * 更新为默认状态 (0),若操作失败则跳过本次循环
                     */
                    if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    // 唤醒后继节点
                    unparkSuccessor(h);
                }
                /*
                 * 如果 h 节点状态为默认状态 (0),
                 * 则需要使用 CAS 操作来将 h 节点状态设置为 PROPAGATE(-3),
                 * 用以保证对后继节点进行唤醒传播
                 */
                else if (ws == 0 &&
                         !compareAndSetWaitStatus(h, 0, Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            /*
             * 如果 h 节点仍然为头部节点,则结束循环 
             * 如果头部节点已改变,则重新进行循环
             */
            if (h == head)                   // loop if head changed
                break;
        }
    }
    
    • 唤醒头部节点的后继节点或设置头部节点状态为传播状态 (PROPAGATE(-3))

    • 后继节点被唤醒后,会尝试获取共享锁,获取成功之后,又会调用 setHeadAndPropagate(Node node, int propagate) 方法,将唤醒操作传播下去

    • 该方法保证了队列中处于等待状态的节点能够有办法被唤醒

  • shouldParkAfterFailedAcquire(Node pred, Node node)

    private static boolean shouldParkAfterFailedAcquire(Node pred, Node node) {
        // 获取前驱节点状态
        int ws = pred.waitStatus;
        if (ws == Node.SIGNAL)
            /*
             * 如果前驱节点状态为 SIGNAL(-1),则表示其在释放锁的时候
             * 会去唤醒后继节点,所以此时后继节点可以阻塞自己,等待被唤醒
             */
            return true;
        if (ws > 0) {
            /*
             * 如果前驱节点状态为 CANCELLED(1),则在队列中向前遍历,
             * 直到找到第一个非 CANCELLED(1) 状态的节点,并将该节点
             * 设置为当前节点的前驱节点
             */
            do {
                node.prev = pred = pred.prev;
            } while (pred.waitStatus > 0);
            pred.next = node;
        } else {
            /*
             * 如果前驱节点状态既不为 SIGNAL(-1) 也不为 CANCELLED(1),
             * 则使用 CAS 操作将前驱节点状态设置为 SIGNAL(-1)
             */
            compareAndSetWaitStatus(pred, ws, Node.SIGNAL);
        }
        return false;
    }
    
    • 根据前驱节点状态来判断是否要阻塞当前节点
  • parkAndCheckInterrupt()

    private final boolean parkAndCheckInterrupt() {
        // 阻塞当前线程
        LockSupport.park(this);
        // 检查当前线程是否被中断
        return Thread.interrupted();
    }
    
    • 阻塞当前线程并检查其是否被中断
  • cancelAcquire(Node node)

    private void cancelAcquire(Node node) {
        // 忽略当前节点不存在的情况
        if (node == null)
            return;
    
        // 将当前节点的线程设置为 null
        node.thread = null;
    
        // 将 pred 节点的引用指向当前节点的前驱节点
        Node pred = node.prev;
        /*
         * 如果前驱节点状态为 CANCELLED(1),则在队列中向前遍历,
         * 直到找到第一个非 CANCELLED(1) 状态的节点,并将该节点
         * 设置为当前节点的前驱节点
         */
        while (pred.waitStatus > 0)
            node.prev = pred = pred.prev;
    
        // 将 predNext 节点的引用指向 pred 节点的后继节点
        Node predNext  = pred.next;
    
        // 将当前节点状态设置为 CANCELLED(1)
        node.waitStatus = Node.CANCELLED;
    
        /*
         * 如果当前节点是尾部节点,且使用 CAS 操作将尾部节点
         * 由当前节点更新为 pred 节点,成功后,再使用 CAS 操作
         * 将 pred 节点的后继节点由 predNext 节点更新为 null
         *
         * 此时就断开了 pred 节点与其所有后继节点的联系,这些后继
         * 节点在引用链上不可达,最终会被 GC 回收掉
         */
        if (node == tail && compareAndSetTail(node, pred)) {
            compareAndSetNext(pred, predNext, null);
        /*
         * 如果当前节点不是尾部节点 (即当前节点还存在着后继节点),
         * 此时要做的事是将 pred 节点和当前节点的非 CANCELLED(1)
         * 状态的后继节点拼接起来
         */
        } else {
            int ws;
            /*
             * 如果 pred 节点不是头部节点,
             * 且 (pred 节点状态为 SIGNAL(-1);或如果 pred 节点状态
             *      不为 SIGNAL(-1),则将其设置为 SIGNAL(-1))
             * 且 pred 节点的线程不为 null
             */
            if (pred != head &&
                ((ws = pred.waitStatus) == Node.SIGNAL ||
                 (ws <= 0 && compareAndSetWaitStatus(pred, ws, Node.SIGNAL))) &&
                pred.thread != null) {
                // 将 next 节点的引用当前节点的后继节点
                Node next = node.next;
                /*
                 * 如果 next 节点不为 null 且 next 节点状态不为 CANCELLED(1)
                 * 则使用 CAS 操作将 pred 节点的后继节点由 predNext 节点更新为 next 节点
                 */
                if (next != null && next.waitStatus <= 0)
                    compareAndSetNext(pred, predNext, next);
            /*
             * 如果不满足上诉条件,则在这种情况下,
             * 为了保证队列的活跃性,需要去唤醒一次后继节点
             * 
             * 举例说明:
             *      如果 pred 节点是头部节点,则有可能当前已经
             *      没有线程持有锁了,也就不会有释放锁唤醒后继节点
             *      的操作,而如果不唤醒后继节点,队列就挂掉了
             */
            } else {
                unparkSuccessor(node);
            }
    
            /*
             * 将当前节点的后继节点设置为其本身,之所以不设置为 null,
             * 是因为为了方便 AQS 中 Condition 部分的 isOnSyncQueue 方法
             *
             * isOnSyncQueue:
             *      用于判断一个原先属于条件队列的节点是否转移到了同步队列上,
             *      因为同步队列中会用到节点的 next 域,如果节点状态为 CANCELLED(1)
             *      且其 next 域也有值的话,则可以说明该节点一定位于同步队列上
             *
             * 在 GC 层面,和设置为 null 具有相同的效果
             */
            node.next = node; // help GC
        }
    }
    
    • 取消当前节点获取锁的尝试
  • unparkSuccessor(Node node)

    private void unparkSuccessor(Node node) {
        // 获取当前节点状态
        int ws = node.waitStatus;
        /*
         * 如果当前节点状态不为 CANCELLED(1) 或默认状态 (0)
         * 则使用 CAS 操作将当前节点状态设置为默认状态 (0)
         */
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);
    
        // 将 s 节点的引用指向当前节点的后继节点
        Node s = node.next;
        // 如果 s 节点为 null 或 s 节点状态为 CANCELLED(1)
        if (s == null || s.waitStatus > 0) {
            // 将 s 节点设置为 null
            s = null;
            /*
             * 在队列中向前遍历,直到找到第一个非 CANCELLED(1) 
             * 状态的节点并将 s 节点的引用指向该节点
             */
            for (Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        // 如果 s 节点不为 null,则将其唤醒
        if (s != null)
            LockSupport.unpark(s.thread);
    }
    
    • 如果当前节点存在后继节点,则将其唤醒

2、acquireUninterruptibly() 相关方法
  • acquireUninterruptibly()

    public void acquireUninterruptibly() {
        sync.acquireShared(1);
    }
    
    • 获取许可,阻塞当前线程,直到获取到许可或线程被中断为止,忽略中断

3、acquire(int permits) 相关方法
  • acquire(int permits)

    public void acquire(int permits) throws InterruptedException {
        // 如果获取许可数目小于 0,则抛出异常
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }
    
    • 获取指定数目的许可,阻塞当前线程,直到获取到许可或线程被中断为止

4、acquireUninterruptibly(int permits) 相关方法
  • acquireUninterruptibly(int permits)

    public void acquireUninterruptibly(int permits) {
        // 如果获取许可数目小于 0,则抛出异常
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireShared(permits);
    }
    
    • 获取指定数目的许可,阻塞当前线程,直到获取到许可或线程被中断为止,忽略中断

5、tryAcquire() 相关方法
  • tryAcquire()

    public boolean tryAcquire() {
        // 调用内部类 Sync 的 nonfairTryAcquireShared(int acquires) 方法来获取许可
        return sync.nonfairTryAcquireShared(1) >= 0;
    }
    
    • 尝试获取许可,如果获取成功,则返回 true;如果获取失败,则返回 false

6、tryAcquire(long timeout, TimeUnit unit) 相关方法
  • tryAcquire(long timeout, TimeUnit unit)

    public boolean tryAcquire(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }
    
    • 尝试获取许可,在指定的等待时间内,如果获取成功,则返回 true;如果获取失败,则返回 false

7、release() 相关方法
  • release()

    public void release() {
        sync.releaseShared(1);
    }
    
    • 释放许可
  • releaseShared(int arg)

    public final boolean releaseShared(int arg) {
        // 尝试释放共享锁
        if (tryReleaseShared(arg)) {
            // 唤醒后继节点
            doReleaseShared();
            return true;
        }
        return false;
    }
    
    • 释放共享锁
  • tryReleaseShared(int arg)

    protected boolean tryReleaseShared(int arg) {
        throw new UnsupportedOperationException();
    }
    
    • 尝试释放共享锁,该方法没有具体实现,交由子类实现,此处实际调用的是 Semaphore 内部类 Sync 中的 tryReleaseShared(int releases) 方法 (当前线程释放许可,并将许可数目加 1)

8、release(int permits) 相关方法
  • release(int permits)

    public void release(int permits) {
        // 如果释放许可数目小于 0,则抛出异常
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }
    
    • 释放指定数目的许可

9、availablePermits() 相关方法
  • availablePermits()

    public int availablePermits() {
        return sync.getPermits();
    }
    
    • 获取当前许可数目

10、drainPermits() 相关方法
  • drainPermits()

    public int drainPermits() {
        return sync.drainPermits();
    }
    
    • 获取当前许可数目,并将当前许可数目设置为 0

11、reducePermits(int reduction) 相关方法
  • reducePermits(int reduction)

    protected void reducePermits(int reduction) {
        // 如果减少许可数目小于 0,则抛出异常
        if (reduction < 0) throw new IllegalArgumentException();
        sync.reducePermits(reduction);
    }
    
    • 减少当前许可数目

12、isFair() 相关方法
  • isFair()

    public boolean isFair() {
        return sync instanceof FairSync;
    }
    
    • 判断当前使用的是公平模式还是非公平模式

    • 如果使用公平模式,则返回 true;如果使用非公平模式,则返回 false


13、hasQueuedThreads() 相关方法
  • hasQueuedThreads()

    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }
    
    • 查询是否有线程正在等待获取许可,如果有,则返回 true;如果没有,则返回 false

    • 由于线程随时可能会被取消等待,因此返回 true 并不能保证任何其他线程会获取到许可

    • 此方法主要用于监视系统状态


14、getQueueLength() 相关方法
  • getQueueLength()

    public final int getQueueLength() {
        return sync.getQueueLength();
    }
    
    • 获取正在等待获取许可的线程数目的估计值

    • 该值只是一个估计值,因为此方法在遍历内部数据结构时,线程数量可能会发生变化

    • 此方法主要用于监视系统状态,而不用于同步控制


五、举例说明

  • SemaphoreTest.java

    public class SemaphoreTest {
    
        static int permits = 2;
        static int count = 0;
    
        private void go() {
            Semaphore semaphore = new Semaphore(permits);
            for (int i = 0; i < 8; i++) {
                new Thread(new SemaphoreTestTask(semaphore), "Thread" + i).start();
            }
        }
    
        public static void main(String[] args) {
            new SemaphoreTest().go();
            
            // 线程【Thread0】开始工作
            // 线程【Thread1】开始工作
            // 线程【Thread1】完成工作
            // 线程【Thread0】完成工作
            // ===============================================
            // 线程【Thread4】开始工作
            // 线程【Thread5】开始工作
            // 线程【Thread5】完成工作
            // 线程【Thread4】完成工作
            // ===============================================
            // 线程【Thread2】开始工作
            // 线程【Thread3】开始工作
            // 线程【Thread3】完成工作
            // 线程【Thread2】完成工作
            // ===============================================
            // 线程【Thread6】开始工作
            // 线程【Thread7】开始工作
            // 线程【Thread6】完成工作
            // 线程【Thread7】完成工作
            // ===============================================
        }
    }
    
    class SemaphoreTestTask implements Runnable {
    
        private Semaphore semaphore;
    
        public SemaphoreTestTask(Semaphore semaphore) {
            this.semaphore = semaphore;
        }
    
        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println("线程【" + Thread.currentThread().getName() + "】开始工作");
                TimeUnit.SECONDS.sleep(1);
                System.out.println("线程【" + Thread.currentThread().getName() + "】完成工作");
                if (++SemaphoreTest.count == SemaphoreTest.permits) {
                    System.out.println("===============================================");
                    SemaphoreTest.count = 0;
                }
                TimeUnit.SECONDS.sleep(1);
                semaphore.release();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
    
    • 如上所示,调用 acquire() 方法会获取许可,获取到了许可的线程才能继续执行,没有获取到许可的线程需要阻塞等待,直到许可被释放为止

六、参考资料

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值