JDK源码分析系列.Semaphore

本文深入解析了Semaphore的概念和工作原理,展示了其在控制线程访问资源时的重要作用,包括公平性和非公平性的区别,以及如何用于互斥锁和死锁恢复。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

/**
 * 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.  Each {@link #release} adds a permit,
 * potentially releasing a blocking acquirer.
 * However, no actual permit objects are used; the {@code Semaphore} just
 * keeps a count of the number available and acts accordingly.
 *一个计数的信号量,从概念上说,一个信号维持了一组许可证。如果需要,每个acquire方法(需求方)都会阻碍住(相当于减一的操作),直到这个凭证是可用的,然后才能去得到它。每个release,都会增加一个凭证,意味着的会释放给一个阻碍的需求方。
 * <p>Semaphores are often used to restrict the number of threads than can
 * access some (physical or logical) resource. For example, here is
 * a class that uses a semaphore to control access to a pool of items:
信号量通常被用在限制能够访问(物理或者逻辑)资源的线程数目。比如,这里的一个类使用一个信号量去控制访问池里面的元素。
 *  <pre> {@code
 * class Pool {
 *   private static final int MAX_AVAILABLE = 100;
    //定义一个资源限制,池中元素的数量。
 *   private final Semaphore available = new Semaphore(MAX_AVAILABLE, true);
 * 
 *   public Object getItem() throws InterruptedException {
      //阻碍住一个许可证此时这个凭证不可用了。可以理解为一个资源占用。
 *     available.acquire();
 *     return getNextAvailableItem();
 *   }
 *
 *   public void putItem(Object x) {
 *     if (markAsUnused(x))
       //增加许可证。可用资源增加了。
 *       available.release();
 *   }
 *
 *   // Not a particularly efficient data structure; just for demo
 *
 *   protected Object[] items = ... whatever kinds of items being managed
 *   protected boolean[] used = new boolean[MAX_AVAILABLE];
 *//可以理解下面模型为简单的标记清除模型。
 *   protected synchronized Object getNextAvailableItem() {
 *     for (int i = 0; i < MAX_AVAILABLE; ++i) {
 *       if (!used[i]) {
 *          used[i] = true;
 *          return items[i];
 *       }
 *     }
 *     return null; // not reached
 *   }
 *
 *   protected synchronized boolean markAsUnused(Object item) {
 *     for (int i = 0; i < MAX_AVAILABLE; ++i) {
 *       if (item == items[i]) {
 *          if (used[i]) {
 *            used[i] = false;
 *            return true;
 *          } else
 *            return false;
 *       }
 *     }
 *     return false;
 *   }
 * }}</pre>
 *
 * <p>Before obtaining an item each thread must acquire a permit from
 * the semaphore, guaranteeing that an item is available for use. When
 * the thread has finished with the item it is returned back to the
 * pool and a permit is returned to the semaphore, allowing another
 * thread to acquire that item.  Note that no synchronization lock is
 * held when {@link #acquire} is called as that would prevent an item
 * from being returned to the pool.  The semaphore encapsulates the
 * synchronization needed to restrict access to the pool, separately
 * from any synchronization needed to maintain the consistency of the
 * pool itself.
 *在获取元素之前,每个线程必须从信号量中获取许可,才能保证这个一个元素是可用的。
   当这个线程用完了这个元素然后将它扔到池里,并将许可证返回给信号量,再让其他线程去获取这个元素。注意就是:
 当acquire调用时是没有持有同步锁的,如果持有的话元素就不能回到池中。信号量封装了限制访问池所需的同步(本身继承的同步类使用的是AQS,啥是AQS?下章讲解),与维护池本身的一致性所需的任何同步是分开的。
 * <p>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.  This is more commonly known as a <em>binary
 * semaphore</em>, because it only has two states: one permit
 * available, or zero permits available.  When used in this way, the
 * binary semaphore has the property (unlike many {@link java.util.concurrent.locks.Lock}
 * implementations), that the &quot;lock&quot; can be released by a
 * thread other than the owner (as semaphores have no notion of
 * ownership).  This can be useful in some specialized contexts, such
 * as deadlock recovery.
 *初始化一个信号量可以被用来做互斥锁,因为该信号量最多有一个许可证被用。这个一般被称为二进制信号量,因为,它只有两种状态,
 一个是许可证可以被用,一个是没有许可证被用。当我们使用这种方式的时候,这个二进制信号量的属性与其他的锁不一样。它可以由不是它的所持有者释放(因为信号量没有所有者这一个概念)。在一些指定的上下文中非常有用,如死锁恢复。
 * <p> The constructor for this class optionally accepts a
 * <em>fairness</em> parameter. When set false, this class makes no
 * guarantees about the order in which threads acquire permits. In
 * particular, <em>barging</em> 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. 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). 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.
这个类的构造方法可以随意的接受一个fairness参数,当设置为false的时候,这个类将不会保证关于获取许可证的顺序。详细说明,barging是允许的,也就是说一个线程调用acquire方法能够在等待之前分配许可证,逻辑是,新的线程会将它自己放置在这个等待队列的最前端。当fairness设置为true的时候,这个信号量将保证任何一个线程调用任何一个acquire是被选中,以便按照处理这些方法调用的顺序获得许可。(这里主要讲了参数设置为TRUE和false的区别)。注意,这个FIFO排序必须引用在指定内部的执行节点。所以,一个线程有可能在另一个线程之前调用acquire,除非是一个接一个到达执行点,从方法返回也是如此。还需要注意的是不定时的tryAcquire方法不准守公平的设置,但会接受任何可用的许可。
 *
 * <p>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.
 通常来说,信号量被用在控制资源的访问应该被初始化为fair。以确保没有任何线程在访问资源的时候陷入饥饿。
 当用信号量控制其他类型的同步,公平排序通常比不公平吞吐量的优势的比重更加值得考虑。

 * <p>This class also provides convenience methods to {@link
 * #acquire(int) acquire} and {@link #release(int) release} multiple
 * permits at a time.  Beware of the increased risk of indefinite
 * postponement when these methods are used without fairness set true.

这个类也提供了方便的方法acquire,release(int)一次多个许可证。当这些方法没有在公平设置为true的时候,当心会增加不确定延迟的风险。
 * <p>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.
 内存一致性的影响,调用前线程操作release()方法。动作允许一个成功的acquire方法在其他线程中。

 * @since 1.5
 * @author Doug Lea
 */
public class Semaphore implements java.io.Serializable {
    private static final long serialVersionUID = -3222578661600680210L;
    private final Sync sync;
    abstract static class Sync extends AbstractQueuedSynchronizer {
        private static final long serialVersionUID = 1192457210091910933L;

        Sync(int permits) {
//这个操作为volatile的内存语义写
            setState(permits);
        }

        final int getPermits() {
//这个操作为volatile的内存语义读
            return getState();
        }

        final int nonfairTryAcquireShared(int acquires) {
            for (;;) {
                int available = getState();
                //可用的减去获得的计算还剩多少
                int remaining = available - acquires;
                 //AQS方法比较更新
                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;
            }
        }
    }

//构造方法,许可证个数,及采用公平和非公平策略
    public Semaphore(int permits, boolean fair) {
        sync = fair ? new FairSync(permits) : new NonfairSync(permits);
    }
//从信号量获取一个许可,如果无可用许可前 将一直阻塞等待,
    public void acquire() throws InterruptedException {
        sync.acquireSharedInterruptibly(1);
    }

   //从信号量尝试获取一个许可,如果无可用许可,直接返回false,不会阻塞
    public boolean tryAcquire() {
        return sync.nonfairTryAcquireShared(1) >= 0;
    }

    //在指定的时间内尝试从信号量中获取许可,如果在指定的时间内获取成功,返回true,否则返回false
    public boolean tryAcquire(long timeout, TimeUnit unit)
        throws InterruptedException {
        return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout));
    }

    //释放一个许可
    public void release() {
        sync.releaseShared(1);
    }

    //从信号量获取指定数目许可,如果无可用许可前 将一直阻塞等待,
    public void acquire(int permits) throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireSharedInterruptibly(permits);
    }

   //
    public void acquireUninterruptibly(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.acquireShared(permits);
    }

   //从信号量尝试获取一定数目许可,如果无可用许可,直接返回false,不会阻塞
    public boolean tryAcquire(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.nonfairTryAcquireShared(permits) >= 0;
    }

    //在指定的时间内尝试从信号量中获取一定数目许可,如果在指定的时间内获取成功,返回true,否则返回false
    public boolean tryAcquire(int permits, long timeout, TimeUnit unit)
        throws InterruptedException {
        if (permits < 0) throw new IllegalArgumentException();
        return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout));
    }

   //释放一定数量的许可
    public void release(int permits) {
        if (permits < 0) throw new IllegalArgumentException();
        sync.releaseShared(permits);
    }

 //获取当前信号量可用的许可,这个主要是用在判断现在可用的有多少个,主要用于调试。
    public int availablePermits() {
        return sync.getPermits();
    }

   //可获取并返回立即可用的所有许可个数,并且将可用许可置0。
   //  if (current == 0 || compareAndSetState(current, 0))
    public int drainPermits() {
        return sync.drainPermits();
    }

 //减少许可数量
    protected void reducePermits(int reduction) {
        if (reduction < 0) throw new IllegalArgumentException();
        sync.reducePermits(reduction);
    }

  //判断当前采用的何种策略。
    public boolean isFair() {
        return sync instanceof FairSync;
    }

  //返回是否存在正在等待的线程。
    public final boolean hasQueuedThreads() {
        return sync.hasQueuedThreads();
    }

   //等待的数量
    public final int getQueueLength() {
        return sync.getQueueLength();
    }

   //等待的集合
    protected Collection<Thread> getQueuedThreads() {
        return sync.getQueuedThreads();
    }

   //
    public String toString() {
        return super.toString() + "[Permits = " + sync.getPermits() + "]";
    }
}



Semaphore运用例子,:

public void testSparkSubmitVmShutsDown() throws Exception {
    ChildProcAppHandle handle = LauncherServer.newAppHandle();
    TestClient client = null;
//产生一个非公平的semaphore
    final Semaphore semaphore = new Semaphore(0);
    try {
//定义一个网络连接
      Socket s = new Socket(InetAddress.getLoopbackAddress(),
        LauncherServer.getServerInstance().getPort());
//添加一个监听
      handle.addListener(new SparkAppHandle.Listener() {
        public void stateChanged(SparkAppHandle handle) {
          semaphore.release();
        }
        public void infoChanged(SparkAppHandle handle) {
          semaphore.release();
        }
      });
      client = new TestClient(s);
      client.send(new Hello(handle.getSecret(), "1.4.0"));
//尝试获取信号
      assertTrue(semaphore.tryAcquire(30, TimeUnit.SECONDS));
      // Make sure the server matched the client to the handle.
      assertNotNull(handle.getConnection());
      close(client);
      assertTrue(semaphore.tryAcquire(30, TimeUnit.SECONDS));
      assertEquals(SparkAppHandle.State.LOST, handle.getState());
    } finally {
      kill(handle);
      close(client);
      client.clientThread.join();
    }
  }
  


 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值