Atomic Classes in Java
Introduction
The java.util.concurrent.atomic package provides a toolkit of classes that support lock-free, thread-safe programming on single variables. These classes are typically more performant than using traditional synchronized blocks or ReentrantLock because they utilize low-level hardware instructions known as Compare-And-Swap (CAS).
Key Concepts
- Atomic Operations: An action that happens entirely or not at all. Unlike standard operations (like
count++), which involve a read-modify-write sequence that can be interrupted by other threads, atomic operations are performed as a single, uninterruptible step. - Lock-Free: Atomic classes avoid the overhead of thread suspension, context switching, and potential deadlocks associated with traditional locking mechanisms.
- CAS (Compare-And-Swap): The core mechanism where the CPU checks if a variable still holds an expected value before updating it. If it has changed (due to another thread), the operation typically retries until it succeeds.
Commonly Used Classes
AtomicInteger,AtomicLong,AtomicBoolean: Used for thread-safe operations on single primitive types (e.g., counters, flags).AtomicReference: Provides atomic updates to object references.- Array Variants (
AtomicIntegerArray, etc.): Extend atomic operation support to specific elements within an array. - Accumulator Classes (
LongAdder,DoubleAdder): Introduced in Java 8, these are highly efficient for scenarios where multiple threads update a common sum (like collecting statistics). They reduce contention by maintaining internal arrays of variables.
Code Example: Without vs. With Atomic
The Problem: Standard Variables (Not Thread-Safe)
If multiple threads run this code, count++ may lose updates due to race conditions.
public class Counter {
private int count = 0;
public void increment() {
count++; // NOT ATOMIC! Includes Read -> Modify -> Write
}
}
The Solution: Using AtomicInteger
By using an Atomic class, we guarantee thread-safety without needing the synchronized keyword.
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicCounter {
private AtomicInteger count = new AtomicInteger(0);
public void increment() {
// Atomically increments by one using CAS
count.incrementAndGet();
}
public int getCount() {
return count.get();
}
}
Best Practice Tip
Prefer Atomic classes over synchronized blocks when you only need to update a single variable concurrently. However, if you need to perform complex operations involving multiple variables that must be updated together (a compound action), you should use higher-level synchronization tools like synchronized blocks or ReentrantLock.