Backend Development 9 min read

Understanding Java Thread Locks: synchronized, ReentrantLock, Semaphore, and AtomicInteger

This article explains why multithreading is needed, outlines the challenges of concurrent access, and provides a detailed overview of four Java thread lock mechanisms—synchronized, ReentrantLock, Semaphore, and AtomicInteger—along with their characteristics, usage patterns, and performance considerations.

Mike Chen's Internet Architecture
Mike Chen's Internet Architecture
Mike Chen's Internet Architecture
Understanding Java Thread Locks: synchronized, ReentrantLock, Semaphore, and AtomicInteger

Multithreading is favored over creating separate processes because it consumes less memory and switches faster, sharing the same address space among threads, which makes it a more economical way to achieve concurrent tasks.

However, sharing resources introduces challenges: when multiple threads access the same resource, data consistency must be maintained, leading to the need for synchronization mechanisms.

Java offers several lock mechanisms:

synchronized

ReentrantLock

Semaphore

AtomicInteger (and related atomic classes)

synchronized is a built‑in keyword that locks a shared resource, ensuring that only one thread executes the synchronized block or method at a time, providing atomicity and visibility. Since Java 1.6 it has been optimized with lightweight locks, lock elimination, and bias locking, though a thread waiting on a synchronized lock cannot be interrupted.

ReentrantLock implements the Lock interface, offering features such as interruptible lock acquisition, timed lock attempts, and fairness policies. It performs similarly to synchronized under low contention but scales better under high concurrency. Locks must be released manually, typically in a finally block:

Lock lock = new ReentrantLock(); try { lock.lock(); // ... perform task } finally { lock.unlock(); }

ReentrantLock supports fair and non‑fair modes; non‑fair is the default because it is usually more efficient.

Semaphore acts as a counting lock, allowing a configurable number of threads to acquire permits simultaneously, which is useful when multiple resources need to be protected. It also supports interruptible acquisition, timed waits, and fairness settings, and like ReentrantLock, permits must be released explicitly, preferably in a finally block.

AtomicInteger and other atomic classes provide lock‑free, thread‑safe operations for single variables (e.g., increment). They are implemented using low‑level CPU instructions, offering higher performance than ReentrantLock for simple atomic updates, though they cannot synchronize multiple variables together.

In practice, prefer synchronized for simple synchronization due to its readability and JVM‑handled unlocking; switch to ReentrantLock or Semaphore when high contention or advanced features (fairness, interruptibility) are required, and use atomic classes for lightweight, single‑variable updates.

Beyond code‑level concurrency, high‑throughput systems also rely on architectural techniques such as caching (Redis), CDNs, and asynchronous messaging to handle large loads.

JavaconcurrencySemaphoreReentrantLocksynchronizedAtomicIntegerThread Locks
Mike Chen's Internet Architecture
Written by

Mike Chen's Internet Architecture

Over ten years of BAT architecture experience, shared generously!

0 followers
Reader feedback

How this landed with the community

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