JUC's Semaphore

Brief introduction

Semaphore is a semaphore to control access to shared resources of the plurality of counters, and as CountDownLatch, which is essentially a "shared lock."

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

如有必要,在许可可用前会阻塞每一个 acquire,然后再获取该许可。
每个 release 添加一个许可,从而可能释放一个正在阻塞的获取者。
复制代码

However, no actual permit objects, Semaphore only the number of available licenses is counted, and act accordingly.

Semaphore commonly used to limit the number of threads can access certain resources (physical or logical) is.

Here we have a simple example to illustrate the parking lot of Semaphore:

  1. For simplicity we assume that only 5 parking spaces parking lot. Beginning no vehicle parking spaces all all empty, and then has the arrival of three vehicles, enough parking spaces, parking arrangements go, then three again, this time because only two parking spaces, all only two stops, a must rest was waiting outside until the free parking spaces. Of course, after each one we need to designate the outside. When parking the car out there, there are seats available, then arrange for a car to go (as to which vehicles, depends on the mechanism of selection is fair or unfair).

  2. From the perspective of the program, the equivalent parking semaphore Semaphore, wherein the license number of 5, the vehicle relative to the thread. As to a car, license number will be minus one. When the parking lot is no parking space (license number == 0), other vehicles need to be outside waiting. If you have a car drove out of the parking, license number + 1, then put to a car.

  3. Semaphore semaphore is a non-negative integer (> = 1). When a thread wants to access a shared resource, it must first obtain Semaphore. When Semaphore> 0, the acquisition of the resource and Semaphore - 1. If the S emaphore value = 0, then all shared resources have been fully occupied by another thread, the thread must wait for the other thread to release resources. When the thread release resources, Semaphore is +1.

Achieve analysis

java.util.concurrent.Semaphore structure as shown below:

As can be seen from the figure, an internal lock Semaphore comprising fair (FairSync) and unfair lock (NonfairSync), internal class inheritance Sync, wherein Sync succession AQS (again illustrated the importance of the AQS).

Semaphore provides two constructors:

Semaphore(int permits) :创建具有给定的许可数和非公平的公平设置的 Semaphore 。
Semaphore(int permits, boolean fair) :创建具有给定的许可数和给定的公平设置的 Semaphore 。
复制代码

To achieve the following:

public Semaphore(int permits) {
    sync = new NonfairSync(permits);
}

public Semaphore(int permits, boolean fair) {
    sync = fair ? new FairSync(permits) : new NonfairSync(permits);
}
复制代码

Semaphore select a non-default fair locks.

When the semaphore Semaphore = 1, it can be used as a mutex. Which is equivalent to 0, it states:

  1. When = 1, other threads may be acquired;
  2. When = 0, exclusively, that is, other threads must wait.

Semaphore implementation structure of the code, and the like ReentrantLock.

Semaphore acquisition

Semaphore provides #acquire () method to obtain a license.

public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
复制代码

The internal call AQS #acquireSharedInterruptibly (int arg) a method of acquiring synchronization state in shared mode. code show as below:

public final void acquireSharedInterruptibly(int arg)
        throws InterruptedException {
    if (Thread.interrupted())
        throw new InterruptedException();
    if (tryAcquireShared(arg) < 0)
        doAcquireSharedInterruptibly(arg);
}
复制代码

In #acquireSharedInterruptibly (int arg) method will be called #tryAcquireShared (int arg) method. And #tryAcquireShared (int arg) method, implemented by subclasses. For Semaphore, if we choose non-equity modes, NonfairSync of #tryAcquireShared (int arg) method is called or the call FairSync of #tryAcquireShared (int arg) method. If #tryAcquireShared (int arg) method returns <0, it will block waiting to perform blocking at insufficient Semaphore semaphore, as follows:

// AQS.java
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;
                }
            }
            /**
             * 对于 Semaphore 而言,如果 tryAcquireShared 返回小于 0 时,则会阻塞等待。
             */
            if (shouldParkAfterFailedAcquire(p, node) &&
                    parkAndCheckInterrupt())
                throw new InterruptedException();
        }
    } finally {
        if (failed)
            cancelAcquire(node);
    }
}
复制代码

In addition, Semaphore This is why when you use AQS, state representatives that the remaining number of licenses available, rather than the number of licenses already in use. We assume that state represents the number of licenses already in use, then the result #tryAcquireShared (int arg) = return of the original number of licenses - state, this operation in the concurrent case, there is a problem thread safe. So, state representatives that the remaining number of licenses available, rather than the number of licenses already in use.

FairSync method inequity implementation code is as follows:

// FairSync.java
@Override
protected int tryAcquireShared(int acquires) {
    for (;;) {
        //判断该线程是否位于CLH队列的列头,从而实现公平锁
        if (hasQueuedPredecessors())
            return -1;
        //获取当前的信号量许可
        int available = getState();

        //设置“获得acquires个信号量许可之后,剩余的信号量许可数”
        int remaining = available - acquires;

        //CAS设置信号量
        if (remaining < 0 ||
                compareAndSetState(available, remaining))
            return remaining;
    }
}
复制代码

By #hasQueuedPredecessors () method, the thread is located determines a column head CLH queue, in order to achieve a fair lock. NonfairSync method implemented unfair situations, as follows:

// NonfairSync.java
protected int tryAcquireShared(int acquires) {
    return nonfairTryAcquireShared(acquires);
}

// Sync.java
final int nonfairTryAcquireShared(int acquires) {
    for (;;) {
        int available = getState();
        int remaining = available - acquires;
        if (remaining < 0 ||
            compareAndSetState(available, remaining))
            return remaining;
    }
}
复制代码

For non-fair terms, because it does not determine whether the current thread is located CLH synchronization queue column headers, so will be relatively simple.

Semaphore is released

Get a license, you need to run out when released, Semaphore offers #release () method to release the license. code show as below:

public void release() {
    sync.releaseShared(1);
}
复制代码

AQS internal call of #releaseShared (int arg) method, the release of sync.

// AQS.java
public final boolean releaseShared(int arg) {
    if (tryReleaseShared(arg)) {
        doReleaseShared();
        return true;
    }
    return false;
}
复制代码

releaseShared (int arg) method will be called Sync Semaphore inner classes of #tryReleaseShared (int arg) method, the release of sync.

// Sync.java
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");
        //设置可获取的信号许可数为next
        if (compareAndSetState(current, next))
            return true;
    }
}
复制代码

If, when the method returns true, the release represents the synchronization status is successful, so #releaseShared (int args) method, call #doReleaseShared () method, Wake block waiting for permission Semaphore thread.

Application Examples

We have parking as an example:

public class SemaphoreTest {

    static class Parking {
    
        //信号量
        private Semaphore semaphore;

        Parking(int count) {
            semaphore = new Semaphore(count);
        }

        public void park() {
            try {
                //获取信号量
                semaphore.acquire();
                long time = (long) (Math.random() * 10);
                System.out.println(Thread.currentThread().getName() + "进入停车场,停车" + time + "秒..." );
                Thread.sleep(time);
                System.out.println(Thread.currentThread().getName() + "开出停车场...");
            } catch (InterruptedException e) {
                e.printStackTrace();
            } finally {
                semaphore.release();
            }
        }
    }


    static class Car extends Thread {
        Parking parking ;

        Car(Parking parking){
            this.parking = parking;
        }

        @Override
        public void run() {
            parking.park();     //进入停车场
        }
    }

    public static void main(String[] args){
        Parking parking = new Parking(3);

        for(int i = 0 ; i < 5 ; i++){
            new Car(parking).start();
        }
    }
}
复制代码

Results are as follows:

Guess you like

Origin juejin.im/post/5d8b184151882534b02f17d0