博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
J.U.C并发框架源码阅读(五)Semaphore
阅读量:4980 次
发布时间:2019-06-12

本文共 34317 字,大约阅读时间需要 114 分钟。

基于版本jdk1.7.0_80

java.util.concurrent.Semaphore

 

代码如下

/* * ORACLE PROPRIETARY/CONFIDENTIAL. Use is subject to license terms. * * * * * * * * * * * * * * * * * * * * *//* * * * * * * Written by Doug Lea with assistance from members of JCP JSR-166 * Expert Group and released to the public domain, as explained at * http://creativecommons.org/publicdomain/zero/1.0/ */package java.util.concurrent;import java.util.*;import java.util.concurrent.locks.*;import java.util.concurrent.atomic.*;/** * 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. * *

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: *

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

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. * *

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 binary * semaphore, 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 Lock} * implementations), that the "lock" 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. * *

The constructor for this class optionally accepts a * fairness parameter. When set false, this class makes no * guarantees about the order in which threads acquire permits. 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. 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. * *

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. 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()} * happen-before * actions following a successful "acquire" method such as {
@code acquire()} * in another thread. * * @since 1.5 * @author Doug Lea * */public class Semaphore implements java.io.Serializable { private static final long serialVersionUID = -3222578661600680210L; /** All mechanics via AbstractQueuedSynchronizer subclass */ 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; } } } /** * Creates a {
@code Semaphore} with the given number of * permits and nonfair fairness setting. * * @param permits the initial number of permits available. * This value may be negative, in which case releases * must occur before any acquires will be granted. */ public Semaphore(int permits) { sync = new NonfairSync(permits); } /** * Creates a {
@code Semaphore} with the given number of * permits and the given fairness setting. * * @param permits the initial number of permits available. * This value may be negative, in which case releases * must occur before any acquires will be granted. * @param fair {
@code true} if this semaphore will guarantee * first-in first-out granting of permits under contention, * else {
@code false} */ public Semaphore(int permits, boolean fair) { sync = fair ? new FairSync(permits) : new NonfairSync(permits); } /** * Acquires a permit from this semaphore, blocking until one is * available, or the thread is {
@linkplain Thread#interrupt interrupted}. * *

Acquires a permit, if one is available and returns immediately, * reducing the number of available permits by one. * *

If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of two things happens: *

    *
  • Some other thread invokes the {
    @link #release} method for this * semaphore and the current thread is next to be assigned a permit; or *
  • Some other thread {
    @linkplain Thread#interrupt interrupts} * the current thread. *
* *

If the current thread: *

    *
  • has its interrupted status set on entry to this method; or *
  • is {
    @linkplain Thread#interrupt interrupted} while waiting * for a permit, *
* then {
@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * * @throws InterruptedException if the current thread is interrupted */ public void acquire() throws InterruptedException { sync.acquireSharedInterruptibly(1); } /** * Acquires a permit from this semaphore, blocking until one is * available. * *

Acquires a permit, if one is available and returns immediately, * reducing the number of available permits by one. * *

If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * some other thread invokes the {

@link #release} method for this * semaphore and the current thread is next to be assigned a permit. * *

If the current thread is {

@linkplain Thread#interrupt interrupted} * while waiting for a permit then it will continue to wait, but the * time at which the thread is assigned a permit may change compared to * the time it would have received the permit had no interruption * occurred. When the thread does return from this method its interrupt * status will be set. */ public void acquireUninterruptibly() { sync.acquireShared(1); } /** * Acquires a permit from this semaphore, only if one is available at the * time of invocation. * *

Acquires a permit, if one is available and returns immediately, * with the value {

@code true}, * reducing the number of available permits by one. * *

If no permit is available then this method will return * immediately with the value {

@code false}. * *

Even when this semaphore has been set to use a * fair ordering policy, a call to {

@code tryAcquire()} will * immediately acquire a permit if one is available, whether or not * other threads are currently waiting. * This "barging" behavior can be useful in certain * circumstances, even though it breaks fairness. If you want to honor * the fairness setting, then use * {
@link #tryAcquire(long, TimeUnit) tryAcquire(0, TimeUnit.SECONDS) } * which is almost equivalent (it also detects interruption). * * @return {
@code true} if a permit was acquired and {
@code false} * otherwise */ public boolean tryAcquire() { return sync.nonfairTryAcquireShared(1) >= 0; } /** * Acquires a permit from this semaphore, if one becomes available * within the given waiting time and the current thread has not * been {
@linkplain Thread#interrupt interrupted}. * *

Acquires a permit, if one is available and returns immediately, * with the value {

@code true}, * reducing the number of available permits by one. * *

If no permit is available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of three things happens: *

    *
  • Some other thread invokes the {
    @link #release} method for this * semaphore and the current thread is next to be assigned a permit; or *
  • Some other thread {
    @linkplain Thread#interrupt interrupts} * the current thread; or *
  • The specified waiting time elapses. *
* *

If a permit is acquired then the value {

@code true} is returned. * *

If the current thread: *

    *
  • has its interrupted status set on entry to this method; or *
  • is {
    @linkplain Thread#interrupt interrupted} while waiting * to acquire a permit, *
* then {
@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * *

If the specified waiting time elapses then the value {

@code false} * is returned. If the time is less than or equal to zero, the method * will not wait at all. * * @param timeout the maximum time to wait for a permit * @param unit the time unit of the {
@code timeout} argument * @return {
@code true} if a permit was acquired and {
@code false} * if the waiting time elapsed before a permit was acquired * @throws InterruptedException if the current thread is interrupted */ public boolean tryAcquire(long timeout, TimeUnit unit) throws InterruptedException { return sync.tryAcquireSharedNanos(1, unit.toNanos(timeout)); } /** * Releases a permit, returning it to the semaphore. * *

Releases a permit, increasing the number of available permits by * one. If any threads are trying to acquire a permit, then one is * selected and given the permit that was just released. That thread * is (re)enabled for thread scheduling purposes. * *

There is no requirement that a thread that releases a permit must * have acquired that permit by calling {

@link #acquire}. * Correct usage of a semaphore is established by programming convention * in the application. */ public void release() { sync.releaseShared(1); } /** * Acquires the given number of permits from this semaphore, * blocking until all are available, * or the thread is {
@linkplain Thread#interrupt interrupted}. * *

Acquires the given number of permits, if they are available, * and returns immediately, reducing the number of available permits * by the given amount. * *

If insufficient permits are available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * one of two things happens: *

    *
  • Some other thread invokes one of the {
    @link #release() release} * methods for this semaphore, the current thread is next to be assigned * permits and the number of available permits satisfies this request; or *
  • Some other thread {
    @linkplain Thread#interrupt interrupts} * the current thread. *
* *

If the current thread: *

    *
  • has its interrupted status set on entry to this method; or *
  • is {
    @linkplain Thread#interrupt interrupted} while waiting * for a permit, *
* then {
@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * Any permits that were to be assigned to this thread are instead * assigned to other threads trying to acquire permits, as if * permits had been made available by a call to {
@link #release()}. * * @param permits the number of permits to acquire * @throws InterruptedException if the current thread is interrupted * @throws IllegalArgumentException if {
@code permits} is negative */ public void acquire(int permits) throws InterruptedException { if (permits < 0) throw new IllegalArgumentException(); sync.acquireSharedInterruptibly(permits); } /** * Acquires the given number of permits from this semaphore, * blocking until all are available. * *

Acquires the given number of permits, if they are available, * and returns immediately, reducing the number of available permits * by the given amount. * *

If insufficient permits are available then the current thread becomes * disabled for thread scheduling purposes and lies dormant until * some other thread invokes one of the {

@link #release() release} * methods for this semaphore, the current thread is next to be assigned * permits and the number of available permits satisfies this request. * *

If the current thread is {

@linkplain Thread#interrupt interrupted} * while waiting for permits then it will continue to wait and its * position in the queue is not affected. When the thread does return * from this method its interrupt status will be set. * * @param permits the number of permits to acquire * @throws IllegalArgumentException if {
@code permits} is negative * */ public void acquireUninterruptibly(int permits) { if (permits < 0) throw new IllegalArgumentException(); sync.acquireShared(permits); } /** * Acquires the given number of permits from this semaphore, only * if all are available at the time of invocation. * *

Acquires the given number of permits, if they are available, and * returns immediately, with the value {

@code true}, * reducing the number of available permits by the given amount. * *

If insufficient permits are available then this method will return * immediately with the value {

@code false} and the number of available * permits is unchanged. * *

Even when this semaphore has been set to use a fair ordering * policy, a call to {

@code tryAcquire} will * immediately acquire a permit if one is available, whether or * not other threads are currently waiting. This * "barging" behavior can be useful in certain * circumstances, even though it breaks fairness. If you want to * honor the fairness setting, then use {
@link #tryAcquire(int, * long, TimeUnit) tryAcquire(permits, 0, TimeUnit.SECONDS) } * which is almost equivalent (it also detects interruption). * * @param permits the number of permits to acquire * @return {
@code true} if the permits were acquired and * {
@code false} otherwise * @throws IllegalArgumentException if {
@code permits} is negative */ public boolean tryAcquire(int permits) { if (permits < 0) throw new IllegalArgumentException(); return sync.nonfairTryAcquireShared(permits) >= 0; } /** * Acquires the given number of permits from this semaphore, if all * become available within the given waiting time and the current * thread has not been {
@linkplain Thread#interrupt interrupted}. * *

Acquires the given number of permits, if they are available and * returns immediately, with the value {

@code true}, * reducing the number of available permits by the given amount. * *

If insufficient permits are available then * the current thread becomes disabled for thread scheduling * purposes and lies dormant until one of three things happens: *

    *
  • Some other thread invokes one of the {
    @link #release() release} * methods for this semaphore, the current thread is next to be assigned * permits and the number of available permits satisfies this request; or *
  • Some other thread {
    @linkplain Thread#interrupt interrupts} * the current thread; or *
  • The specified waiting time elapses. *
* *

If the permits are acquired then the value {

@code true} is returned. * *

If the current thread: *

    *
  • has its interrupted status set on entry to this method; or *
  • is {
    @linkplain Thread#interrupt interrupted} while waiting * to acquire the permits, *
* then {
@link InterruptedException} is thrown and the current thread's * interrupted status is cleared. * Any permits that were to be assigned to this thread, are instead * assigned to other threads trying to acquire permits, as if * the permits had been made available by a call to {
@link #release()}. * *

If the specified waiting time elapses then the value {

@code false} * is returned. If the time is less than or equal to zero, the method * will not wait at all. Any permits that were to be assigned to this * thread, are instead assigned to other threads trying to acquire * permits, as if the permits had been made available by a call to * {
@link #release()}. * * @param permits the number of permits to acquire * @param timeout the maximum time to wait for the permits * @param unit the time unit of the {
@code timeout} argument * @return {
@code true} if all permits were acquired and {
@code false} * if the waiting time elapsed before all permits were acquired * @throws InterruptedException if the current thread is interrupted * @throws IllegalArgumentException if {
@code permits} is negative */ public boolean tryAcquire(int permits, long timeout, TimeUnit unit) throws InterruptedException { if (permits < 0) throw new IllegalArgumentException(); return sync.tryAcquireSharedNanos(permits, unit.toNanos(timeout)); } /** * Releases the given number of permits, returning them to the semaphore. * *

Releases the given number of permits, increasing the number of * available permits by that amount. * If any threads are trying to acquire permits, then one * is selected and given the permits that were just released. * If the number of available permits satisfies that thread's request * then that thread is (re)enabled for thread scheduling purposes; * otherwise the thread will wait until sufficient permits are available. * If there are still permits available * after this thread's request has been satisfied, then those permits * are assigned in turn to other threads trying to acquire permits. * *

There is no requirement that a thread that releases a permit must * have acquired that permit by calling {

@link Semaphore#acquire acquire}. * Correct usage of a semaphore is established by programming convention * in the application. * * @param permits the number of permits to release * @throws IllegalArgumentException if {
@code permits} is negative */ public void release(int permits) { if (permits < 0) throw new IllegalArgumentException(); sync.releaseShared(permits); } /** * Returns the current number of permits available in this semaphore. * *

This method is typically used for debugging and testing purposes. * * @return the number of permits available in this semaphore */ public int availablePermits() { return sync.getPermits(); } /** * Acquires and returns all permits that are immediately available. * * @return the number of permits acquired */ public int drainPermits() { return sync.drainPermits(); } /** * Shrinks the number of available permits by the indicated * reduction. This method can be useful in subclasses that use * semaphores to track resources that become unavailable. This * method differs from {

@code acquire} in that it does not block * waiting for permits to become available. * * @param reduction the number of permits to remove * @throws IllegalArgumentException if {
@code reduction} is negative */ protected void reducePermits(int reduction) { if (reduction < 0) throw new IllegalArgumentException(); sync.reducePermits(reduction); } /** * Returns {
@code true} if this semaphore has fairness set true. * * @return {
@code true} if this semaphore has fairness set true */ public boolean isFair() { return sync instanceof FairSync; } /** * Queries whether any threads are waiting to acquire. Note that * because cancellations may occur at any time, a {
@code true} * return does not guarantee that any other thread will ever * acquire. This method is designed primarily for use in * monitoring of the system state. * * @return {
@code true} if there may be other threads waiting to * acquire the lock */ public final boolean hasQueuedThreads() { return sync.hasQueuedThreads(); } /** * Returns an estimate of the number of threads waiting to acquire. * The value is only an estimate because the number of threads may * change dynamically while this method traverses internal data * structures. This method is designed for use in monitoring of the * system state, not for synchronization control. * * @return the estimated number of threads waiting for this lock */ public final int getQueueLength() { return sync.getQueueLength(); } /** * Returns a collection containing threads that may be waiting to acquire. * Because the actual set of threads may change dynamically while * constructing this result, the returned collection is only a best-effort * estimate. The elements of the returned collection are in no particular * order. This method is designed to facilitate construction of * subclasses that provide more extensive monitoring facilities. * * @return the collection of threads */ protected Collection
getQueuedThreads() { return sync.getQueuedThreads(); } /** * Returns a string identifying this semaphore, as well as its state. * The state, in brackets, includes the String {
@code "Permits ="} * followed by the number of permits. * * @return a string identifying this semaphore, as well as its state */ public String toString() { return super.toString() + "[Permits = " + sync.getPermits() + "]"; }}

View Code

 

0. Semaphore简介

Semaphore也是一个线程同步的辅助类,可以维护当前访问自身的线程个数,并提供了同步机制。使用Semaphore可以控制同时访问资源的线程个数,例如,实现一个文件允许的并发访问数。

Semaphore的主要方法摘要:

  void acquire():从此信号量获取一个许可,在提供一个许可前一直将线程阻塞,否则线程被中断。

  void release():释放一个许可,将其返回给信号量。

  int availablePermits():返回此信号量中当前可用的许可数。

  boolean hasQueuedThreads():查询是否有线程正在等待获取。

 

1. Semaphore原理概述

Semaphore利用AQS的state变量维护了信号量的计数,一旦发现acquire操作会导致state小于0,就阻塞当前线程,直到前驱线程释放了足够的信号量,才会唤醒当前线程继续执行。

release操作则会更新state变量,表示释放信号量的语义,同时还会去AQS的等待队列里检查,尝试唤醒后续的等待线程。

Semaphore提供了公平与非公平的实现方式。

 

2. Semaphore.acquire方法的执行轨迹

默认情况下,Semaphore采用的是非公平实现。

Semaphore.acquire    public void acquire() throws InterruptedException {        sync.acquireSharedInterruptibly(1);    }AbstractQueuedSynchronizer.acquireSharedInterruptibly    public final void acquireSharedInterruptibly(int arg)            throws InterruptedException {        if (Thread.interrupted())            throw new InterruptedException();        if (tryAcquireShared(arg) < 0)            doAcquireSharedInterruptibly(arg);    }Semaphore.NonfairSync.tryAcquireShared        protected int tryAcquireShared(int acquires) {            return nonfairTryAcquireShared(acquires);        }Semaphore.Sync.nonfairTryAcquireShared        final int nonfairTryAcquireShared(int acquires) {            for (;;) {                int available = getState();                int remaining = available - acquires;                if (remaining < 0 ||                    compareAndSetState(available, remaining))//尝试插队抢占信号量                    return remaining;            }        }AbstractQueuedSynchronizer.doAcquireSharedInterruptibly    /**     * Acquires in shared interruptible mode.     * @param arg the acquire argument     */    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);                        p.next = null; // help GC                        failed = false;                        return;                    }                }                if (shouldParkAfterFailedAcquire(p, node) &&                    parkAndCheckInterrupt())                    throw new InterruptedException();            }        } finally {            if (failed)                cancelAcquire(node);        }    }

很经典的逻辑,除了自定义的tryAcquireShared方法以外,与上一篇提到的CountDownLatch.await方法的调用轨迹并无区别,在此不赘述其逻辑了

非公平的语义体现在Semaphore.Sync.nonfairTryAcquireShared方法中,会尝试直接用cas操作更新AQS维护的state变量,这是一个插队的行为。

 

3. Semaphore.release方法的执行轨迹

 

Semaphore.release    public void release() {        sync.releaseShared(1);    }AbstractQueuedSynchronizer.releaseShared    /**     * Releases in shared mode.  Implemented by unblocking one or more     * threads if {
@link #tryReleaseShared} returns true. * * @param arg the release argument. This value is conveyed to * {
@link #tryReleaseShared} but is otherwise uninterpreted * and can represent anything you like. * @return the value returned from {
@link #tryReleaseShared} */ public final boolean releaseShared(int arg) { if (tryReleaseShared(arg)) { doReleaseShared(); return true; } return false; }Semaphore.Sync.tryReleaseShared protected final boolean tryReleaseShared(int releases) { for (;;) {
//在无限循环中用cas操作更新state变量 int current = getState(); int next = current + releases; if (next < current) // overflow throw new Error("Maximum permit count exceeded"); if (compareAndSetState(current, next)) return true; } }AbstractQueuedSynchronizer.doReleaseShared /** * Release action for shared mode -- signal successor and ensure * propagation. (Note: For exclusive mode, release just amounts * to calling unparkSuccessor of head if it needs signal.) */ 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; // loop to recheck cases unparkSuccessor(h); } else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) continue; // loop on failed CAS } if (h == head) // loop if head changed break; } }

除了自定义的Semaphore.Sync.tryReleaseShared方法以外,与上一篇提到的CountDownLatch.countDown方法的调用轨迹并无区别,在此不赘述其逻辑了

 

4. Semaphore的公平语义

Semaphore.FairSync     /**     * 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())//如果AQS维护的等待队列中有线程在排队,则拒绝执行tryAcquire操作,直接走后续的线程排队等待流程                    return -1;                int available = getState();//用cas操作占领信号量的操作,只在AQS维护的等待队列为空时才会进行,这样就实现了公平语义                int remaining = available - acquires;                if (remaining < 0 ||                    compareAndSetState(available, remaining))                    return remaining;            }        }    }

从Semaphore.FairSync的源码可以看出,在调用tryAcquireShared方法时,如果AQS维护的等待队列中有线程在排队,则拒绝执行tryAcquire操作,直接走后续的线程排队等待流程。如果等待队列为空,也就是无竞争的情况下,才会尝试用cas操作去更新state变量。

这样就实现了公平语义。

 

转载于:https://www.cnblogs.com/stevenczp/p/7154896.html

你可能感兴趣的文章
PHP 生成图片缩略图函数
查看>>
Boost Bimap示例
查看>>
ESLint 使用入门
查看>>
流水作业调度
查看>>
涨姿势系列之——内核环境下内存映射函数
查看>>
遍历数组批量更新数组里元素的某一项属性
查看>>
github 收藏项目的方法
查看>>
九的余数
查看>>
北京师范大学第十五届ACM决赛-重现赛K Keep In Line ( 字符串模拟实现)
查看>>
(转)C# — WinForm 消息框的使用
查看>>
时间管理(转)
查看>>
Future FutrueTask Callable类源码说明以及原理使用
查看>>
flask 外键关系和多对多查询
查看>>
接收行数,打印平行四边形
查看>>
Linux上coredump调试:call stack栈顶函数地址为0 分析实战
查看>>
Educational Codeforces Round 11——C. Hard Process(YY)
查看>>
0054 Spring MVC的@Controller和@RequestMapping注解
查看>>
C#学习总结
查看>>
python字符串实战
查看>>
SQL学习笔记之B+树的几点总结
查看>>