Mastering Java Semaphore: How init, down, and up Ensure Thread Safety

This article explains the semaphore model, its three core operations (init, down, up), how they manage counters and waiting queues, and demonstrates proper usage in Java to achieve mutual exclusion and resource limiting, complete with code examples and visual diagrams.

JavaEdge
JavaEdge
JavaEdge
Mastering Java Semaphore: How init, down, and up Ensure Thread Safety

Semaphore Model Overview

The semaphore is a synchronization primitive consisting of a counter and a waiting queue that are invisible to external code; they can only be accessed via three provided methods.

Three Core Methods

init() – Sets the initial value of the counter.

down() – Decrements the counter; if the result is less than 0, the calling thread is blocked.

up() – Increments the counter; if the result is ≤ 0, one thread is removed from the waiting queue and awakened. The condition uses >0 to indicate that no thread is blocked, so only ≤ 0 triggers a wake‑up.

Correct Usage Pattern

Call down() to acquire the semaphore (lock).

Perform the critical section work.

Call up() to release the semaphore.

If the semaphore is initialized to 1, a concurrent scenario should never see the counter > 0 unless up() is called prematurely, which defeats the purpose of a semaphore.

Atomic Guarantees

All three methods are atomic; the JDK implementation ( java.util.concurrent.Semaphore) guarantees this.

Java Code Example

In Java, acquire() and release() correspond to down() and up() respectively.

Consider two threads t1 and t2 calling acquire() on a semaphore with an initial count of 1: t1 succeeds, counter becomes 0, and proceeds. t2 sees the counter become -1 and is blocked.

When t1 finishes and calls release(), the counter goes from -1 to 0, satisfying the ≤ 0 condition, so t2 is awakened and can enter the critical section, guaranteeing mutual exclusion.

Why Not Just Use a Lock?

A lock provides mutual exclusion only. A semaphore can also limit the number of concurrent threads accessing a resource, making it suitable for resource pools such as database connection pools.

Implementing a Simple Rate Limiter

Set the semaphore counter to the number of resources N in the pool. Each thread calls down() before using a resource and up() after releasing it.

Note: The example uses Vector for the pool, which is not thread‑safe for add/remove operations; a proper concurrent collection should be used.

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.

JavaBackend Developmentsemaphoreinterviewthread synchronization
JavaEdge
Written by

JavaEdge

First‑line development experience at multiple leading tech firms; now a software architect at a Shanghai state‑owned enterprise and founder of Programming Yanxuan. Nearly 300k followers online; expertise in distributed system design, AIGC application development, and quantitative finance investing.

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.