Java Atomic vs Volatile vs Synchronized

Last Updated : 17 Dec, 2025

When working with multithreading in Java, ensuring thread safety becomes crucial. Java provides different mechanisms, such as synchronized, volatile, and atomic variables, to handle shared data across threads. While all three help in managing concurrent access, they differ significantly in what they provide:

  • synchronized ensures mutual exclusion and visibility.
  • volatile ensures visibility only (not synchronization or mutual exclusion).
  • atomic variables provide lock-free, thread-safe operations for specific actions.

Synchronized

Synchronized keyword ensures that only one thread at a time can execute a particular method or block of code on a given object. It provides:

  • Mutual exclusion: one thread at a time executes the synchronized code.
  • Visibility: changes made by one thread become visible to others after the lock is released.
Java
public class GFG{
    private int count = 0;
    public synchronized void increment(){
        // atomic due to synchronization
        count++; 
    }

    public int getCount() { return count; }
    public static void main(String[] args)
        throws InterruptedException
    {
        GFG demo = new GFG();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++)
                demo.increment();
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++)
                demo.increment();
        });
        
        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("Final count (synchronized): " + demo.getCount());
    }
}

Output
Final count (synchronized): 2000

Explanation: The synchronized keyword ensures only one thread executes increment() at a time, guaranteeing atomicity and visibility.

Volatile

The volatile keyword in Java ensures that all threads have a consistent view of a variable's value. It prevents caching of the variable's value by threads, ensuring that updates to the variable are immediately visible to other threads.

Java
public class VolatileExample {

    // ensures visibility, not atomicity
    private volatile int count = 0; 
    public void increment(){
        // not atomic (read + modify + write)
        count++; 
    }

    public static void main(String[] args)
        throws InterruptedException
    {
        VolatileExample demo = new VolatileExample();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++)
                demo.increment();
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++)
                demo.increment();
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("Final count (volatile): " + demo.count);
    }
}

Output
Final count (volatile): 2000

Explanation: Even though count is volatile, the operation count++ is not atomic — it consists of three steps: read, increment, and write. Thus, multiple threads can interleave these steps, leading to incorrect results.

Atomic keyword 

Atomic Variables provide lock-free, thread-safe operations on single variables. They ensure atomicity and visibility using low-level Compare-And-Swap (CAS) operations without using synchronization.

Java
import java.util.concurrent.atomic.AtomicInteger;

public class GFG{

    private AtomicInteger count = new AtomicInteger(0);
    public void increment()
    {
        count.incrementAndGet(); // atomic, lock-free
                                 // increment
    }
    public int getCount() { return count.get(); }
    public static void main(String[] args)
        throws InterruptedException
    {
        GFG demo = new GFG();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++)
                demo.increment();
        });

        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++)
                demo.increment();
        });

        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("Final count (AtomicInteger): " + demo.getCount());
    }
}

Output
Final count (AtomicInteger): 2000

Explanation: AtomicInteger ensures atomic updates through CAS operations. It achieves thread safety without locks, making it more efficient than synchronized for simple variable updates.

Synchronized vs Volatile vs Atomic

FeatureSynchronizedVolatileAtomic
Applies toMethods/blocksVariablesVariables
PurposeEnsures mutual exclusion and consistency (via locks)Ensures visibility (no atomicity)Provides atomic operations (no locks)
PerformanceLower (due to locking)Higher than synchronizedHigher than both synchronized and volatile
ConcurrencyProne to deadlocks/livelocksImmune (no locks)Immune (no locks)
Comment