Java provides atomic operations through classes in the java.util.concurrent.atomic package. These classes ensure that operations on variables are performed atomically without needing explicit synchronization.
Atomic operations are actions performed in a single step without interference from other threads. This means that an atomic operation is indivisible and uninterruptible, guaranteeing data integrity even in multi-threaded environments.
Why Atomic Operations
When multiple threads operate on shared data, there’s a risk of race conditions, where the final outcome depends on the timing of thread execution. Atomic operations eliminate this risk by ensuring that no thread sees an intermediate or inconsistent state.
Java’s Atomic Classes (from java.util.concurrent.atomic)
Java provides a set of atomic classes that support lock-free, thread-safe programming on single variables. These classes internally use low-level CPU instructions for atomicity, instead of traditional synchronization mechanisms like synchronized.
Here are the most common atomic classes:
1. AtomicInteger
Performs atomic operations on int values.
AtomicInteger atomicInt = new AtomicInteger(0);
atomicInt.incrementAndGet(); // Atomically increments by 1
atomicInt.addAndGet(5); // Atomically adds 5Code language: JavaScript (javascript)
2. AtomicLong
Performs atomic operations on long values.
AtomicLong atomicLong = new AtomicLong(100L);
atomicLong.decrementAndGet(); // Atomically decrements by 1Code language: JavaScript (javascript)
3. AtomicBoolean
For atomic operations on boolean values.
AtomicBoolean atomicBool = new AtomicBoolean(false);
atomicBool.compareAndSet(false, true); // Atomically sets to true if current is falseCode language: JavaScript (javascript)
4. AtomicReference<T>
Allows atomic operations on object references.
AtomicReference<String> ref = new AtomicReference<>("Hello");
ref.compareAndSet("Hello", "World"); // Atomically replaces "Hello" with "World"Code language: JavaScript (javascript)
5.AtomicIntegerArray, AtomicLongArray, AtomicReferenceArray
These classes provide atomic operations on arrays of integers, longs, and object references respectively.
They support operations like get, set, compareAndSet, etc.
AtomicIntegerArray atomicIntArray = new AtomicIntegerArray(10);
atomicIntArray.getAndIncrement(5);Code language: JavaScript (javascript)
import java.util.concurrent.atomic.AtomicReferenceArray;
public class AtomicReferenceArrayDemo {
public static void main(String[] args) {
// Create an AtomicReferenceArray with 3 elements
AtomicReferenceArray<String> riverArray = new AtomicReferenceArray<>(3);
// Set initial values
riverArray.set(0, "Ganga");
riverArray.set(1, "Yamuna");
riverArray.set(2, "Saraswati");
// Print original values
System.out.println("Original Rivers:");
for (int i = 0; i < riverArray.length(); i++) {
System.out.println(riverArray.get(i));
}
// Atomically update value at index 1 (if current is "Yamuna")
boolean updated = riverArray.compareAndSet(1, "Yamuna", "Godavari");
// Display result of the update
System.out.println("\nUpdate at index 1 successful: " + updated);
// Print updated values
System.out.println("\nUpdated Rivers:");
for (int i = 0; i < riverArray.length(); i++) {
System.out.println(riverArray.get(i));
}
}
}Code language: JavaScript (javascript)
Common Atomic Methods
| Method | Description |
|---|---|
get() |
Gets the current value atomically |
set(value) |
Sets a new value atomically |
compareAndSet(expected, newValue) |
Sets to newValue if current value equals expected |
getAndIncrement() |
Atomically returns the current value and increments |
incrementAndGet() |
Atomically increments and returns the new value |
lazySet(value) |
Eventually sets to the new value (more efficient than set) |
Program
This program demonstrates the use of various atomic classes provided in the java.util.concurrent.atomic package. The program should illustrate how atomic operations can be performed on different data types such as integers, longs, booleans, and arrays safely and efficiently in a multithreaded environment.
//AllAtomicClassesExample.java
import java.util.concurrent.atomic.*;
public class AllAtomicClassesExample {
public static void main(String[] args) {
// AtomicInteger example
AtomicInteger atomicInt = new AtomicInteger(0);
// Increment operation
System.out.println("Initial value of atomicInt: " + atomicInt.get());
atomicInt.incrementAndGet();
System.out.println("After incrementAndGet(): " + atomicInt.get());
// Compare and set operation
int expectedValue = 1;
int newValue = 10;
boolean exchanged = atomicInt.compareAndSet(expectedValue, newValue);
System.out.println("After compareAndSet(" + expectedValue + ", " + newValue + "): " + atomicInt.get());
System.out.println("Exchanged? " + exchanged);
// AtomicLong example
AtomicLong atomicLong = new AtomicLong(1000);
// Get current value
System.out.println("\nInitial value of atomicLong: " + atomicLong.get());
// Add and get operation
long delta = 500;
long updatedValue = atomicLong.addAndGet(delta);
System.out.println("After addAndGet(" + delta + "): " + updatedValue);
// Get and add operation
long currentValue = atomicLong.getAndAdd(200);
System.out.println("After getAndAdd(200): " + atomicLong.get());
System.out.println("Previous value returned by getAndAdd(): " + currentValue);
// AtomicBoolean example
AtomicBoolean atomicBoolean = new AtomicBoolean(true);
// Get current value
System.out.println("\nInitial value of atomicBoolean: " + atomicBoolean.get());
// Compare and set operation
boolean expectedBoolean = true;
boolean newBoolean = false;
exchanged = atomicBoolean.compareAndSet(expectedBoolean, newBoolean);
System.out.println("After compareAndSet(" + expectedBoolean + ", " + newBoolean + "): " + atomicBoolean.get());
System.out.println("Exchanged? " + exchanged);
// AtomicReference example
AtomicReference<String> atomicReference = new AtomicReference<>("Hello");
// Get current value
System.out.println("\nInitial value of atomicReference: " + atomicReference.get());
// Compare and set operation
String expectedString = "Hello";
String newString = "World";
exchanged = atomicReference.compareAndSet(expectedString, newString);
System.out.println("After compareAndSet(" + expectedString + ", " + newString + "): " + atomicReference.get());
System.out.println("Exchanged? " + exchanged);
// AtomicIntegerArray example
int[] array = {1, 2, 3, 4, 5};
AtomicIntegerArray atomicIntArray = new AtomicIntegerArray(array);
// Get and set operation
int index = 2;
int newValueAtIndex = 10;
int oldValueAtIndex = atomicIntArray.getAndSet(index, newValueAtIndex);
System.out.println("\nAfter getAndSet(" + index + ", " + newValueAtIndex + "): " + atomicIntArray);
System.out.println("Old value at index " + index + ": " + oldValueAtIndex);
}
}
/*
C:\>javac AllAtomicClassesExample.java
C:\>java AllAtomicClassesExample
Initial value of atomicInt: 0
After incrementAndGet(): 1
After compareAndSet(1, 10): 10
Exchanged? true
Initial value of atomicLong: 1000
After addAndGet(500): 1500
After getAndAdd(200): 1700
Previous value returned by getAndAdd(): 1500
Initial value of atomicBoolean: true
After compareAndSet(true, false): false
Exchanged? true
Initial value of atomicReference: Hello
After compareAndSet(Hello, World): World
Exchanged? true
After getAndSet(2, 10): [1, 2, 10, 4, 5]
Old value at index 2: 3
*/Atomic operations and classes in Java provide a robust and efficient way to perform thread-safe computations without explicit synchronization. By leveraging classes like AtomicInteger, AtomicLong, AtomicBoolean, AtomicReference, and array-based variants, developers can ensure atomicity and visibility of shared variables, reducing the complexity and overhead associated with locks. These classes are essential tools in high-performance concurrent applications, enabling safe and scalable updates to shared resources in multithreaded environments.
