Atomic Classes
About
The Atomic classes in Java, part of the java.util.concurrent.atomic
package, provide a way to perform atomic operations on variables. These operations are thread-safe without requiring synchronization, making them a cornerstone of concurrent programming.
Atomic classes are designed to update variables atomically in a multi-threaded environment, ensuring that operations like incrementing, setting, or comparing are performed as a single, indivisible operation.
They use low-level hardware instructions such as Compare-And-Swap (CAS) to ensure thread safety without locks.
They provide a non-blocking alternative to traditional synchronization, resulting in better performance for specific use cases.
Features
Thread Safety: Operations on atomic variables are thread-safe without requiring explicit synchronization.
Non-Blocking: Uses CAS (Compare-And-Swap) to perform updates, avoiding locking mechanisms.
Scalability: Provides better performance and scalability compared to synchronized blocks for fine-grained operations.
Flexible Range of Types: Atomic classes support a variety of data types and structures, such as integers, booleans, arrays, and references.
High Performance: Ideal for low-latency applications where synchronization overhead must be minimized.
Declaration
To use atomic classes, we need to import the java.util.concurrent.atomic
package:
import java.util.concurrent.atomic.*;
Common Atomic Classes
Basic Types:
AtomicBoolean
: Atomic operations for a boolean value.AtomicInteger
: Atomic operations for an integer value.AtomicLong
: Atomic operations for a long value.AtomicReference<V>
: Atomic operations for object references.
Advanced Types:
AtomicIntegerArray
: Atomic operations on arrays of integers.AtomicLongArray
: Atomic operations on arrays of longs.AtomicReferenceArray<E>
: Atomic operations on arrays of references.AtomicStampedReference<V>
: Atomic operations with a version or stamp to prevent the ABA problem.AtomicMarkableReference<V>
: Atomic operations with a boolean marker for reference values.
Key Methods
Get and Set
get()
: Retrieves the current value.set(value)
: Sets the value unconditionally.lazySet(value)
: Sets the value but may delay the write for optimization.
Atomic Updates
getAndIncrement()
: Atomically increments and returns the previous value.getAndDecrement()
: Atomically decrements and returns the previous value.incrementAndGet()
: Atomically increments and returns the new value.decrementAndGet()
: Atomically decrements and returns the new value.
Compare-And-Swap
compareAndSet(expectedValue, newValue)
: Atomically sets a new value if the current value matches the expected value.
Arithmetic and Bitwise
addAndGet(delta)
: Adds a delta to the value atomically.getAndAdd(delta)
: Adds a delta and returns the old value.getAndUpdate(UnaryOperator)
orupdateAndGet(UnaryOperator)
: Applies a function atomically.
Usage
Basic Usage of AtomicInteger
AtomicInteger
import java.util.concurrent.atomic.AtomicInteger;
public class AtomicExample {
public static void main(String[] args) {
AtomicInteger counter = new AtomicInteger(0);
System.out.println("Initial value: " + counter.get());
// Increment
System.out.println("After increment: " + counter.incrementAndGet());
// Add a value
System.out.println("After adding 5: " + counter.addAndGet(5));
// Compare-And-Swap
boolean success = counter.compareAndSet(6, 10);
System.out.println("CAS success: " + success);
System.out.println("Final value: " + counter.get());
}
}
Using AtomicReference
AtomicReference
import java.util.concurrent.atomic.AtomicReference;
public class AtomicReferenceExample {
public static void main(String[] args) {
AtomicReference<String> atomicString = new AtomicReference<>("Initial Value");
System.out.println("Original: " + atomicString.get());
atomicString.set("Updated Value");
System.out.println("Updated: " + atomicString.get());
// Compare-And-Swap
boolean updated = atomicString.compareAndSet("Updated Value", "Final Value");
System.out.println("CAS success: " + updated);
System.out.println("Final: " + atomicString.get());
}
}
Using AtomicIntegerArray
AtomicIntegerArray
import java.util.concurrent.atomic.AtomicIntegerArray;
public class AtomicArrayExample {
public static void main(String[] args) {
int[] values = {1, 2, 3};
AtomicIntegerArray atomicArray = new AtomicIntegerArray(values);
System.out.println("Original value at index 0: " + atomicArray.get(0));
atomicArray.incrementAndGet(0);
System.out.println("After incrementing index 0: " + atomicArray.get(0));
atomicArray.compareAndSet(1, 2, 5);
System.out.println("After CAS at index 1: " + atomicArray.get(1));
}
}
Preventing ABA Problem with AtomicStampedReference
AtomicStampedReference
import java.util.concurrent.atomic.AtomicStampedReference;
public class AtomicStampedExample {
public static void main(String[] args) {
AtomicStampedReference<Integer> atomicStamped = new AtomicStampedReference<>(100, 1);
int stamp = atomicStamped.getStamp();
int value = atomicStamped.getReference();
System.out.println("Original: Value = " + value + ", Stamp = " + stamp);
atomicStamped.compareAndSet(100, 200, stamp, stamp + 1);
System.out.println("Updated: Value = " + atomicStamped.getReference() + ", Stamp = " + atomicStamped.getStamp());
}
}
Applications and Real-World Usage
Counters and Metrics: Use
AtomicInteger
orAtomicLong
for thread-safe counters in web servers, logging systems, and monitoring tools.Non-Blocking Data Structures: Use
AtomicReference
for building lock-free stacks, queues, and other data structures.Preventing ABA Problem: Use
AtomicStampedReference
to handle scenarios where the same value is set multiple times without indicating intermediate changes.Atomic Arrays: Use
AtomicIntegerArray
for managing shared resources in high-concurrency environments.Configuration Updates: Dynamically update configurations in multi-threaded systems using atomic references.
Last updated
Was this helpful?