Semaphore

概述

  A counting semaphore.Conceptually, a semaphore maintains a set of permits. 

    信号量计数器,维护一组许可;

  Each {@link #acquire} blocks if necessary until a permit is available, and then takes it. 

    每次 acquire将被阻塞 直到 许可可用,然后获取它;

  Each {@link #release} adds a permit, potentially releasing a blocking acquirer.

    每次release增加一个许可,释放一个阻塞的获得者;

  However, no actual permit objects are used; the {@code Semaphore} just keeps a count of the number available and acts accordingly.

    没有真实的许可对象被使用;Semaphore仅保持 可用数字的计数 且 依次操作;

 

  Semaphores are often used to restrict the number of threads than can access some (physical or logical) resource. 

    Semaphores 通常被用来 限制访问(物理/逻辑)资源的线程;

  

 

  A semaphore initialized to one, and which is used such that it only has at most one permit available, can serve as a mutual exclusion lock.  

    初始化为 1 的信号量,并且最多只有一个许可证可用,可以用作互斥锁;

  This is more commonly known as a <em>binary semaphore</em>, because it only has two states: one permit available, or zero permits available.  

    初始化为1的信号量,通常称为<em>二进制信号量</em>,因为它只有两种状态:一个可用许可证或零个可用许可证;

  When used in this way, the binary semaphore has the property (unlike many {@link java.util.concurrent.locks.Lock} implementations), that the lock can be released by a thread other than the owner (as semaphores have no notion of ownership).

    当以二进制信号量方式使用,具有以下属性(与{@link java.util.concurrent.locks.Lock} 实现不同),即锁可以由所有者以外的线程释放(因为信号量没有所有权的概念) 

  This can be useful in some specialized contexts, such as deadlock recovery.

    这在某些专用上下文(如死锁恢复)中可能很有用;

 

  The constructor for this class optionally accepts a <em>fairness</em> parameter. 

    此类的构造函数可以选择接受<em>公平性</em>参数

  When set false, this class makes no guarantees about the order in which threads acquire permits. 

    设置为 false 时,此类不保证线程获取许可的顺序;

  In particular, barging is permitted, that is, a thread invoking {@link #acquire} can be allocated a permit ahead of a thread that has been waiting - logically the new thread places itself at the head of the queue of waiting threads. 

    特别是,允许插入,也就是说,调用 {@link #acquire} 的线程可以在一直在等待的线程之前分配一个许可证 - 从逻辑上讲,新线程将自己置于等待线程队列的头部;

  When fairness is set true, the semaphore guarantees that threads invoking any of the {@link #acquire() acquire} methods are selected to obtain permits in the order in which their invocation of those methods was processed (first-in-first-out; FIFO). 

    当公平性设置为 true 时,信号量保证选择调用任何 {@link #acquire() acquire} 方法的线程,以按照处理这些方法的调用顺序(先进先出;先进先出)

  Note that FIFO ordering necessarily applies to specific internal points of execution within these methods. 

  So, it is possible for one thread to invoke {@code acquire} before another, but reach the ordering point after the other, and similarly upon return from the method.

  Also note that the untimed {@link #tryAcquire() tryAcquire} methods do not honor the fairness setting, but will take any permits that are available.

 

  Generally, semaphores used to control resource access should be initialized as fair, to ensure that no thread is starved out from accessing a resource. 

    通常,用于控制资源访问的信号量应初始化为公平,以确保没有线程无法访问资源

  When using semaphores for other kinds of synchronization control, the throughput advantages of non-fair ordering often outweigh fairness considerations.

 

  This class also provides convenience methods to {@link #acquire(int) acquire} and {@link #release(int) release} multiple permits at a time.

    此类还提供了一次 {@link #acquire(int) 获取} 和 {@link #release(int) 释放} 多个许可证的便捷方法

  Beware of the increased risk of indefinite postponement when these methods are used without fairness set true.

    

  Memory consistency effects: Actions in a thread prior to calling a "release" method such as {@code release()} <a href="package-summary.html#MemoryVisibility"><i>happen-before</i></a> actions following a successful "acquire" method such as {@code acquire()} in another thread.

 

应用场景

  用来 限制访问(物理/逻辑)资源的线程;

public static void main(String[] args) {
        Semaphore semaphore = new Semaphore(10);

        Task task = new Task(semaphore);
        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        Thread t3 = new Thread(task);

        t1.start();
        t2.start();
        t3.start();
    }

    static class Task implements  Runnable{

        private Semaphore semaphore;

        Task(Semaphore semaphore){
            this.semaphore = semaphore;
        }

        @Override
        public void run() {
            try {
                semaphore.acquire();
                System.out.println(Thread.currentThread().getName() + "获得许可,剩余许可:" + semaphore.availablePermits());
            } catch (InterruptedException e) {
                e.printStackTrace();
            }finally {
                semaphore.release();
                System.out.println(Thread.currentThread().getName() + "释放许可,剩余许可:" + semaphore.availablePermits());
            }
        }

        public Semaphore getSemaphore() {
            return semaphore;
        }

        public void setSemaphore(Semaphore semaphore) {
            this.semaphore = semaphore;
        }
    }

结果:
Thread-0获得许可,剩余许可:8
Thread-2获得许可,剩余许可:7
Thread-1获得许可,剩余许可:8
Thread-2释放许可,剩余许可:9
Thread-0释放许可,剩余许可:8
Thread-1释放许可,剩余许可:10

  

实现思路 

  继承AQS,以共享模式访问,提供了2种获取方式:

    公平(按队列排列顺序,依次获取)

    非公平(直接获取,不关心队列的等待线程)   

链路 

acquire()

// java.util.concurrent.Semaphore.acquire()
    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

    // java.util.concurrent.locks.AbstractQueuedSynchronizer.acquireSharedInterruptibly (Acquires in shared mode)
    public final void acquireSharedInterruptibly(int arg)
            throws InterruptedException {
        if (Thread.interrupted())
            throw new InterruptedException();
        // 获取失败 -> 将请求的线程加入队列
        if (tryAcquireShared(arg) < 0)
            doAcquireSharedInterruptibly(arg);
    }

    // java.util.concurrent.Semaphore.FairSync.tryAcquireShared (共享模式-公平获取)
    protected int tryAcquireShared(int acquires) {
        for (;;) {
            // Queries whether any threads have been waiting to acquire longer than the current thread.
            // 查询是否有比当前线程等待时间更长的线程,有 -> 获取失败
            if (hasQueuedPredecessors())
                return -1;
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                return remaining;
        }
    }

    // java.util.concurrent.Semaphore.NonfairSync.tryAcquireShared (共享模式-非公平获取)
    protected int tryAcquireShared(int acquires) {
        return nonfairTryAcquireShared(acquires);
    }
    // java.util.concurrent.Semaphore.Sync.nonfairTryAcquireShared (共享模式-非公平获取)
    final int nonfairTryAcquireShared(int acquires) {
        for (;;) {
            // 非公平直接获取
            int available = getState();
            int remaining = available - acquires;
            if (remaining < 0 ||
                    compareAndSetState(available, remaining))
                return remaining;
        }
    }

    // java.util.concurrent.locks.AbstractQueuedSynchronizer.doAcquireSharedInterruptibly
    private void doAcquireSharedInterruptibly(int arg)throws InterruptedException {
        final AbstractQueuedSynchronizer.Node node = addWaiter(AbstractQueuedSynchronizer.Node.SHARED);
        boolean failed = true;
        try {
            for (;;) {
                final AbstractQueuedSynchronizer.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;
                    }
                }
                if (shouldParkAfterFailedAcquire(p, node) &&
                        parkAndCheckInterrupt())
                    throw new InterruptedException();
            }
        } finally {
            if (failed)
                cancelAcquire(node);
        }
    }

  

release()

// java.util.concurrent.Semaphore.release()
    public void release() {
        sync.releaseShared(1);
    }

    // java.util.concurrent.locks.AbstractQueuedSynchronizer.releaseShared (共享模式-释放许可)
    public final boolean releaseShared(int arg) {
        // 如果释放许可成功 -> 唤醒队列中下一个等待的线程
        if (tryReleaseShared(arg)) {
            doReleaseShared();
            return true;
        }
        return false;
    }

    // java.util.concurrent.Semaphore.Sync.tryReleaseShared
    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;
        }
    }

    // java.util.concurrent.locks.AbstractQueuedSynchronizer.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 (;;) {
            AbstractQueuedSynchronizer.Node h = head;
            if (h != null && h != tail) {
                int ws = h.waitStatus;
                if (ws == AbstractQueuedSynchronizer.Node.SIGNAL) {
                    if (!compareAndSetWaitStatus(h, AbstractQueuedSynchronizer.Node.SIGNAL, 0))
                        continue;            // loop to recheck cases
                    unparkSuccessor(h);
                }
                else if (ws == 0 &&
                        !compareAndSetWaitStatus(h, 0, AbstractQueuedSynchronizer.Node.PROPAGATE))
                    continue;                // loop on failed CAS
            }
            if (h == head)                   // loop if head changed
                break;
        }
    }

    // java.util.concurrent.locks.AbstractQueuedSynchronizer.unparkSuccessor
    private void unparkSuccessor(AbstractQueuedSynchronizer.Node node) {
        /*
         * If status is negative (i.e., possibly needing signal) try
         * to clear in anticipation of signalling.  It is OK if this
         * fails or if status is changed by waiting thread.
         */
        int ws = node.waitStatus;
        if (ws < 0)
            compareAndSetWaitStatus(node, ws, 0);

        /*
         * Thread to unpark is held in successor, which is normally
         * just the next node.  But if cancelled or apparently null,
         * traverse backwards from tail to find the actual
         * non-cancelled successor.
         */
        AbstractQueuedSynchronizer.Node s = node.next;
        if (s == null || s.waitStatus > 0) {
            s = null;
            for (AbstractQueuedSynchronizer.Node t = tail; t != null && t != node; t = t.prev)
                if (t.waitStatus <= 0)
                    s = t;
        }
        if (s != null)
            LockSupport.unpark(s.thread);
    }

  

 

posted on 2023-10-25 19:16  anpeiyong  阅读(5)  评论(0编辑  收藏  举报

导航