In Java, atomic variables provide a way to perform thread-safe operations on variables without needing to use explicit synchronization. They ensure that operations on the variable are executed atomically, meaning that they are indivisible and cannot be interrupted by other threads halfway through.
Atomic Variable Classes in Java
Atomic variables in Java are part of the java.util.concurrent.atomic
package and are designed to perform thread-safe operations on single variables without using explicit synchronization (like synchronized
or Lock
).
- Â Atomic Variables:Â Atomic variables are special classes (e.g.,
AtomicInteger
,AtomicLong
,AtomicBoolean
,AtomicReference
, etc.) that provide non-blocking, lock-free thread-safe operations on single values. - Why Use Them?
- Avoids overhead of synchronization.
- Provides atomicity, visibility, and ordering guarantees.
- Improves performance in high-concurrency environments.
- Common Atomic Classes:
AtomicInteger
– for atomic operations onint
AtomicLong
– for atomic operations onlong
AtomicBoolean
– for atomic boolean flagsAtomicReference<T>
– for atomic reference objects- Important Methods:
get()
– returns the current valueset(value)
– sets the valueincrementAndGet()
– increments the value and returns the resultdecrementAndGet()
– decrements and returns the resultcompareAndSet(expect, update)
– atomically sets the value if it equals the expected value
-
Internal Mechanism: Atomic variables rely on low-level atomic CPU instructions (like CAS – Compare-And-Swap) to ensure changes are visible and consistent across threads.
Program on AtomicInteger usage
//AtomicDemo.java import java.util.concurrent.atomic.AtomicInteger; public class AtomicDemo { private AtomicInteger count = new AtomicInteger(0); public void increment() { count.incrementAndGet(); // atomically increments by 1 } public int getCount() { return count.get(); // atomically gets the current value } public static void main(String[] args) { AtomicDemo demo = new AtomicDemo(); Runnable task = () -> { for (int i = 0; i < 1000; i++) { demo.increment(); } }; Thread thread1 = new Thread(task); Thread thread2 = new Thread(task); thread1.start(); thread2.start(); try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Final Count: "+demo.getCount()); // Should print 2000 } } /* C:\>javac AtomicDemo.java C:\>java AtomicDemo Final Count: 2000 */
AtomicReference<T>
Provides atomic operations for a reference to an object of type T. Useful for managing a reference that may be updated atomically.
//AtomicReferenceDemo.java import java.util.concurrent.atomic.AtomicReference; public class AtomicReferenceDemo{ private AtomicReference<String> reference = new AtomicReference<>("initial"); public void update(String newValue) { reference.set(newValue); // atomically updates the reference } public String getValue() { return reference.get(); // atomically gets the current value } public static void main(String[] args) { AtomicReferenceDemo demo= new AtomicReferenceDemo(); Runnable task = () -> { for (int i = 0; i < 3; i++) { demo.update("value_" + i); System.out.println(Thread.currentThread().getName() + " updated value: " + demo.getValue()); } }; Thread thread1 = new Thread(task); Thread thread2 = new Thread(task); thread1.start(); thread2.start(); try { thread1.join(); thread2.join(); } catch (InterruptedException e) { e.printStackTrace(); } System.out.println("Final Value: " + demo.getValue()); // Should print the last updated value } } /* C:\>javac AtomicReferenceDemo.java C:\>java AtomicReferenceDemo Thread-0 updated value: value_0 Thread-1 updated value: value_0 Thread-1 updated value: value_1 Thread-0 updated value: value_1 Thread-1 updated value: value_2 Thread-0 updated value: value_2 Final Value: value_2 */
Benefits of Atomic Variables
- Thread Safety: Operations on atomic variables are atomic and thread-safe.
- No Explicit Synchronization: They avoid the overhead of synchronization locks (synchronized blocks or methods).
- Performance: Designed for high-performance concurrent operations.
When to Use Atomic Variables
- Simple Updates: When you need to perform simple updates (like increments, decrements, or updates to a single variable) without the need for complex synchronization.
- Performance Critical Code: In scenarios where performance overhead from synchronization locks is a concern.
Limitations
- Complex Operations: Atomic variables are designed for simple operations. If you need complex operations that involve more than one variable or state, you might still need to use traditional synchronization methods.
- Not a Panacea: While atomic variables simplify certain types of concurrent operations, they do not eliminate the need for careful design and consideration of thread safety in your application.
Atomic variables in Java provide a straightforward way to handle thread-safe operations on variables without needing explicit synchronization. They are useful for simple operations where performance and simplicity are critical.