Fundamentals 14 min read

Unlocking Java AQS Shared Mode: How Semaphore Works Under the Hood

This article explains the shared‑mode acquisition in Java's AbstractQueuedSynchronizer, walks through the core template methods, compares them with exclusive mode, and demonstrates a classic application by dissecting the Semaphore source code and its usage patterns.

Programmer DD
Programmer DD
Programmer DD
Unlocking Java AQS Shared Mode: How Semaphore Works Under the Hood

Preface

The previous long article on Java AQS queue synchronizer and ReentrantLock laid the groundwork for reading JUC source code; this piece focuses on the shared‑mode acquisition, so readers are encouraged to review the earlier article if needed.

Shared‑Mode Acquisition in AQS

Unlike exclusive mode where only one thread can hold the lock, shared mode allows multiple threads to acquire the synchronization state simultaneously. The key difference is whether multiple threads can obtain the state at the same moment.

Can multiple threads acquire the synchronization state at the same time?

The synchronization state state is maintained inside AQS. The shared‑mode template methods control state similarly to exclusive mode, but they also propagate the state to waiting threads.

Source Code Analysis of Shared‑Mode in AQS

Key methods from the previous article are highlighted (shown in pink) to help recall the shared‑mode flow.

Methods that a custom synchronizer must override

AQS template methods

Key shared‑mode code (with comments) is shown below:

public final void acquireShared(int arg) {<br/>    // Non‑blocking attempt to acquire shared state; if result < 0, acquisition fails<br/>    if (tryAcquireShared(arg) < 0)<br/>        // Enter waiting queue via template method<br/>        doAcquireShared(arg);<br/>}

Entering doAcquireShared:

private void doAcquireShared(int arg) {<br/>    // Create a SHARED node and add it to the wait queue<br/>    final Node node = addWaiter(Node.SHARED);<br/>    boolean failed = true;<br/>    try {<br/>        boolean interrupted = false;<br/>        for (;;) {<br/>            final Node p = node.predecessor();<br/>            if (p == head) {<br/>                int r = tryAcquireShared(arg);<br/>                if (r >= 0) {<br/>                    setHeadAndPropagate(node, r);<br/>                    p.next = null; // help GC<br/>                    if (interrupted) selfInterrupt();<br/>                    failed = false;<br/>                    return;<br/>                }<br/>            }<br/>            if (shouldParkAfterFailedAcquire(p, node) && parkAndCheckInterrupt())<br/>                interrupted = true;<br/>        }<br/>    } finally {<br/>        if (failed) cancelAcquire(node);<br/>    }<br/>}

The crucial difference from exclusive mode lies in the setHeadAndPropagate(node, r) method, which not only sets the head but also propagates the remaining state to other waiting threads.

private void setHeadAndPropagate(Node node, int propagate) {<br/>    Node h = head;<br/>    setHead(node);<br/>    if (propagate > 0 || h == null || h.waitStatus < 0 || (h = head) == null || h.waitStatus < 0) {<br/>        Node s = node.next;<br/>        if (s == null || s.isShared())<br/>            doReleaseShared();<br/>    }<br/>}

The propagation logic ensures that when a thread successfully acquires the state and leaves a positive remainder, waiting threads are notified to try acquiring the remaining permits.

Further analysis of doReleaseShared() shows how the head node’s waitStatus is examined and how waiting successors are unparked.

private void doReleaseShared() {<br/>    for (;;) {<br/>        Node h = head;<br/>        if (h != null && h != tail) {<br/>            int ws = h.waitStatus;<br/>            if (ws == Node.SIGNAL) {<br/>                if (!compareAndSetWaitStatus(h, Node.SIGNAL, 0)) continue;<br/>                unparkSuccessor(h);<br/>            } else if (ws == 0 && !compareAndSetWaitStatus(h, 0, Node.PROPAGATE)) {<br/>                continue;<br/>            }<br/>            if (h == head) break;<br/>        }<br/>    }<br/>}

With the shared‑mode mechanics clarified, the article moves to a classic application: Semaphore .

Semaphore Concept

Semaphore (信号量) can be thought of as a traffic light with two flags: red (stop) and green (go). In Java, tryAcquireShared(arg) returning a non‑negative value means the light is green (permit acquired), otherwise it is red.

Semaphore Source Code Analysis

The class structure mirrors that of ReentrantLock, with a non‑fair synchronizer by default.

Construction sets the initial state (permits) in the AQS super class.

public Semaphore(int permits) {<br/>    sync = new NonfairSync(permits);<br/>}<br/><br/>static final class NonfairSync extends Sync {<br/>    NonfairSync(int permits) {<br/>        super(permits);<br/>    }<br/>}

When permits == 1, Semaphore behaves like a simple mutex, but it can also be used for rate‑limiting by setting a larger permit count.

static int count;<br/>static final Semaphore s = new Semaphore(1);<br/>static void addOne() {<br/>    s.acquire();<br/>    try {<br/>        count += 1;<br/>    } finally {<br/>        s.release();<br/>    }<br/>}

In practice, for high‑performance rate limiting, Guava's RateLimiter is often preferred.

Summary

By linking the shared‑mode template methods with concrete examples like Semaphore, the article shows how to read JUC source code without getting lost, preparing readers for further topics such as ReadWriteLock and CountDownLatch.

Soul‑Searching Questions

When permits is set to 1, Semaphore resembles a simple mutex—what are the key differences compared to ReentrantLock?

How have you used Semaphore in your own projects?

Original Source

Signed-in readers can open the original source through BestHub's protected redirect.

Sign in to view source
Republication Notice

This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactadmin@besthub.devand we will review it promptly.

JavaconcurrencysemaphoreAQSSharedLock
Programmer DD
Written by

Programmer DD

A tinkering programmer and author of "Spring Cloud Microservices in Action"

0 followers
Reader feedback

How this landed with the community

Sign in to like

Rate this article

Was this worth your time?

Sign in to rate
Discussion

0 Comments

Thoughtful readers leave field notes, pushback, and hard-won operational detail here.