Volatile Keyword in Java

Loading

The volatile keyword in Java is used to indicate that a variable’s value will be modified by multiple threads. It ensures visibility and ordering of changes to the variable across threads, preventing issues like stale data and thread interference.


1. Key Features of volatile

  • Visibility: Changes made by one thread to a volatile variable are immediately visible to other threads.
  • Ordering: Ensures that reads and writes to the variable are not reordered by the compiler or CPU.
  • No Atomicity: The volatile keyword does not provide atomicity for compound operations (e.g., incrementing a variable).

2. When to Use volatile

  • When a variable is shared among multiple threads, and its value is updated by one thread and read by others.
  • When the variable’s value does not depend on its current state (e.g., flags, status indicators).
  • When atomicity is not required (use AtomicInteger, AtomicLong, etc., for atomic operations).

3. Example: Using volatile for Visibility

Without volatile, changes made by one thread may not be visible to other threads due to CPU caching.

class SharedResource {
    private volatile boolean flag = false;

    public void setFlag() {
        flag = true; // Write operation
    }

    public void doWork() {
        while (!flag) { // Read operation
            // Busy-wait until flag becomes true
        }
        System.out.println("Flag is now true.");
    }
}

public class VolatileExample {
    public static void main(String[] args) throws InterruptedException {
        SharedResource resource = new SharedResource();

        Thread writerThread = new Thread(() -> {
            try {
                Thread.sleep(1000); // Simulate some work
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            resource.setFlag(); // Set flag to true
        });

        Thread readerThread = new Thread(resource::doWork);

        writerThread.start();
        readerThread.start();

        writerThread.join();
        readerThread.join();
    }
}

In this example:

  • The flag variable is marked as volatile, ensuring that changes made by the writerThread are immediately visible to the readerThread.
  • Without volatile, the readerThread might never see the updated value of flag.

4. volatile vs synchronized

Featurevolatilesynchronized
VisibilityEnsures visibility of changes.Ensures visibility and atomicity.
AtomicityDoes not provide atomicity.Provides atomicity.
PerformanceLightweight.Heavier due to locking.
Use CaseSingle variable updates.Compound operations or critical sections.

5. Limitations of volatile

  • No Atomicity: The volatile keyword does not ensure atomicity for compound operations like i++.
  • Not Suitable for All Scenarios: For complex synchronization, use synchronized blocks or higher-level concurrency utilities like ReentrantLock or Atomic classes.

6. Example: volatile with Atomic Operations

For atomic operations, use classes from the java.util.concurrent.atomic package (e.g., AtomicInteger).

import java.util.concurrent.atomic.AtomicInteger;

class Counter {
    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        count.incrementAndGet(); // Atomic operation
    }

    public int getCount() {
        return count.get();
    }
}

public class AtomicExample {
    public static void main(String[] args) throws InterruptedException {
        Counter counter = new Counter();

        Runnable task = () -> {
            for (int i = 0; i < 1000; i++) {
                counter.increment();
            }
        };

        Thread thread1 = new Thread(task);
        Thread thread2 = new Thread(task);

        thread1.start();
        thread2.start();

        thread1.join();
        thread2.join();

        System.out.println("Count: " + counter.getCount()); // Output: 2000
    }
}

7. Best Practices

  • Use volatile for simple flags or status variables shared across threads.
  • For compound operations, use synchronized or atomic classes.
  • Avoid using volatile for complex synchronization scenarios.
  • Always test multi-threaded code thoroughly to ensure correctness.

By understanding the volatile keyword, you can write thread-safe code that ensures visibility and ordering of shared variables in multi-threaded environments!

Leave a Reply

Your email address will not be published. Required fields are marked *