Semaphore

It is the same as the reentrant lock, which starts with fair lock and unfair lock. The unfair lock is used by default, as shown in the following example:

Public class SemaphoreDemo {public static void main(String[] args) {// Allow 3 threads (3 Spaces) Semaphore Semaphore = new Semaphore(3); // for (int I = 1; i <= 6; I++) {new Thread(() -> {try {// acquire semaphoore.acquire (); System.out.println(thread.currentThread ().getName()) + ); TimeUnit.SECONDS.sleep(2); System.out.println(thread.currentThread ().getName() + "out of parking!!" ); } catch (InterruptedException e) { e.printStackTrace(); } finally {// The semaphore is released, and the waiting thread semaphore.release(); } }, String.valueOf(i)).start(); }}}Copy the code

Acquire (); release(); acquire(); acquire();

Call acquire() with the following source code:

public void acquire() throws InterruptedException {
    sync.acquireSharedInterruptibly(1);
}
Copy the code

Sync is the type defined by Semaphore’s inner class, which is known as AQS. AQS is a very important class that forms the basis of many classes. For example: ReentrantLock, CountDownLatch, ReentrantReadWriteLock, etc., well not to say, today’s topic is semaphores.

Then see acquireSharedInterruptibly (int arg) method

public final void acquireSharedInterruptibly(int arg) throws InterruptedException { if (Thread.interrupted()) throw new InterruptedException(); / / try to access the Shared lock the if (tryAcquireShared (arg) < 0) / / if the return value is less than 0, indicates that the number of licenses have been obtained to complete doAcquireSharedInterruptibly (arg); }Copy the code
protected int tryAcquireShared(int acquires) {
    return nonfairTryAcquireShared(acquires);
}
Copy the code
final int nonfairTryAcquireShared(int acquires) { for (;;) { int available = getState(); Int remaining = available - acquires; int remaining = available - acquires; / / the number of remaining licenses if (remaining < 0 | | / / permit access to complete, Return Remaining compareAndSetState(available, remaining)) // Use CAS to set the number of licenses. }}Copy the code

When a semaphore object is created, if the parameter 3 is passed in, only three threads can obtain the license. The extra threads cannot obtain the license. They must wait for the thread with the license to release the license, and then other threads can grab the license and perform the corresponding task. If, for example: at this point, there are three license, said only three threads can work, if at this time to create threads is more than 3 (such as: 6), so the fourth, fifth, sixth thread, in the first three licensed thread did not release the license, is unable to get license, can only be blocked, waiting for permit.

On the license number is finished, and then enter the doAcquireSharedInterruptibly (arg) method, as shown below:

Private void doAcquireSharedInterruptibly (int arg) throws InterruptedException {/ / this is a thread information encapsulation for the node, to join the synchronous queue, Final Node Node = addWaiter(node.shared); boolean failed = true; try { for (;;) { final Node p = node.predecessor(); If (p == head) {// 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()) // If throws new InterruptedException(); } } finally { if (failed) cancelAcquire(node); }}Copy the code

The above code is for obtaining the license thread, whose thread information will be encapsulated as a node and added to the end of the double-ended queue, as shown in the figure below:

As shown in the figure: since the number of licenses is set to 3, only 3 of the 6 threads will get the licenses. The other three threads will be queued up for synchronization and will only attempt to get the licenses if any threads wake up after the release() method is called.

Next look at the release() method, which releases the license and wakes up the suspended thread.

public void release() {
    sync.releaseShared(1);
}
Copy the code
Public Final Boolean releaseShared(int arg) {if (tryReleaseShared(arg)) {doReleaseShared(); public Final Boolean releaseShared(arg) {doReleaseShared(); Return true; } return false; }Copy the code
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)) // CAS set license number return true; }}Copy the code

In the tryReleaseShared(int Releases) method, CAS guarantees success in setting the number of licenses. Return true to enter the following methods:

private void doReleaseShared() { for (;;) {// Node h = head; if (h ! = null && h ! = tail) {// There are at least two nodes int ws = h.waitStatus; If (ws == node.signal) {// 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 on failed CAS} if (h == head); }}Copy the code

The above code is to wake up the thread. Once the thread is woken up, the thread at the head of the queue (not the head) in the synchronization queue acquires the license and executes the corresponding code. Why is fair lock, because in the queue of threads in the column, only the first node in the subsequent nodes are acquiring a lock, and in obtaining permits, there are other threads (not thread synchronization of column), try to obtain the permit, the two threads are likely to get permits, it is fair to lock. A fair lock means that only the node thread at the head of the queue can acquire the license, while other threads are added to the queue and wait to acquire the license. MMM ~~~, that’s it.