What is the difference between atomic / volatile / synchronized?
Master System Design with Codemia
Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.
When developing multithreaded applications in Java, it is essential to handle thread synchronization carefully to avoid concurrency issues like race conditions, deadlocks, and data inconsistency. Java provides several mechanisms for managing access to shared resources, including atomic variables, the volatile keyword, and synchronized blocks or methods. Each has its own usage and performance implications, and understanding the differences is crucial for effective and efficient multithreaded programming.
Atomic Variables
Atomic variables, as provided by the java.util.concurrent.atomic package, support lock-free thread-safe programming on single variables. These classes include AtomicInteger, AtomicLong, AtomicBoolean, and others. Atomic variables are built using low-level synchronization primitives such as compare-and-swap (CAS), which allow them to be read and written atomically. They are primarily used when updates to a variable are relatively simple and limited to operations like increment, decrement, or setting a new value based on its current state.
For example, an AtomicInteger provides methods like getAndIncrement() and compareAndSet(expectedValue, updateValue) which can be useful for scenarios like generating a sequence of unique identifiers across threads without needing a synchronized block or method.
Volatile Keyword
The volatile keyword in Java is used to indicate that a variable's value will be modified by different threads. Declaring a variable volatile ensures that its value is read from and written to the main memory, and not cached thread-locally. This guarantees visibility of changes to variables across threads.
However, volatile does not provide atomicity. For instance, incrementing a volatile variable (volatileVar++) is not an atomic operation as it involves reading the variable, incrementing it, and then writing it back, which can be interrupted and cause race conditions.
Synchronized Keyword
The synchronized keyword can be used to synchronize method access or particular blocks of code, ensuring that only one thread executes the block or method at any given time. This is more powerful than volatile as it both ensures visibility and atomicity, making it suitable for complex interactions between threads or when one needs to perform multiple operations on shared state atomically.
A synchronized method to increment a count could look like this:
Comparison Table
Here is a comparison of atomic, volatile, and synchronized:
| Feature | Atomic | Volatile | Synchronized |
| Visibility | Yes | Yes | Yes |
| Atomicity | Limited to single variable atomic operations | No | Yes |
| Lock-free | Yes | Yes | No |
| Overhead | Low | Low | High |
When to Use Each?
- Use Atomic classes for variables that are independently modified and do not form part of a combination of operations that need to be atomic together.
- Use Volatile if you need to ensure that updates to a variable are always visible to other threads. It is useful for flag variables like a stop flag in a multithreading context.
- Use Synchronized when you need to perform a sequence of operations that must be atomic or when you need to control the execution of critical sections that operate on shared resources.
In conclusion, choosing the right tool for thread synchronization depends on the specific needs of the application regarding atomicity, visibility, performance overhead, and complexity. Proper usage of atomic, volatile, or synchronized can significantly affect the performance and correctness of a multithreaded application.

