Atomic Variables for Thread safe operations

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


  1.  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.
  2. 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 on int
  • AtomicLong – for atomic operations on long
  • AtomicBoolean – for atomic boolean flags
  • AtomicReference<T> – for atomic reference objects
  • Important Methods:
  • get() – returns the current value
  • set(value) – sets the value
  • incrementAndGet() – increments the value and returns the result
  • decrementAndGet() – decrements and returns the result
  • compareAndSet(expect, update) – atomically sets the value if it equals the expected value
  1. 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.

Scroll to Top