Java
volatile
synchronized
concurrency
multithreading

Difference between volatile and synchronized in Java

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

In Java, writing multithreaded programs often involves managing shared resources and ensuring that concurrent access to these resources does not lead to inconsistent results. Two concepts that play a critical role in this scenario are volatile and synchronized. While both deal with the visibility and access of shared variables, they serve different purposes and are used under different circumstances. In this article, we explore the differences between volatile and synchronized in Java with technical explanations and examples.

Technical Explanation

volatile Keyword

The volatile keyword in Java is used to mark a variable as being stored in the main memory. More importantly, it ensures that any read of the volatile variable always reflects the most recent write by any thread. This addresses the visibility of changes made by one thread to be immediately available to other threads.

Key Points:
  • Visibility Guarantee: Changes to a volatile variable are always visible to other threads.
  • No Atomicity: Operations on volatile variables are not atomic. This means that compound operations (like incrementing) on volatile variables are not thread-safe.
  • No Locks Needed: Access to volatile variables does not require obtaining a lock, resulting in less overhead.
  • Use Cases: Suitable for flags, completion signals, or caching scenarios where simple state checks are needed.
Example:
java
1public class VolatileExample {
2    private volatile boolean flag = false;
3
4    public void toggleFlag() {
5        flag = !flag;
6    }
7
8    public boolean getFlag() {
9        return flag;
10    }
11}

In this example, any changes made to flag in one thread will be immediately visible to other threads accessing it.

synchronized Method/Block

The synchronized keyword is used to control access to a block of code or method. It ensures that only one thread can execute the synchronized code at a time, thus providing both atomicity and visibility.

Key Points:
  • Atomicity and Visibility: synchronized provides both atomicity and visibility guarantees.
  • Locks: A thread must acquire the associated lock to enter a synchronized method/block, which ensures mutual exclusion.
  • Performance Cost: The locking mechanism introduces some overheads, potentially affecting performance.
  • Use Cases: Ideal for critical sections where complex transactions occur, like updating a shared resource.
Example:
java
1public class SynchronizedExample {
2    private int counter = 0;
3
4    public synchronized void incrementCounter() {
5        counter++; // atomic and visible to all threads
6    }
7
8    public synchronized int getCounter() {
9        return counter; 
10    }
11}

In this example, incrementCounter is atomic, meaning one thread can increment the counter without interference from other threads.

Table Summary: volatile vs synchronized

Featurevolatilesynchronized
Visibility ControlEnsures visibility of changesEnsures visibility and atomicity
LockingDoes not use locksManages locks for mutual exclusion
Overhead CostLow due to no lock overheadHigher due to lock acquisition
Use CaseSimple flags or statesComplex transactions

Additional Considerations

When to Use volatile

volatile is suitable in scenarios where a variable is used for checking simple state conditions or flags, and when setting and reading a variable is the only operation needed. It's important to note that if the operation requires more than a simple read or write, volatile may not be sufficient, as it does not guarantee atomicity.

When to Use synchronized

Use synchronized when fine-grained control is needed over accessing a particular piece of code or shared resource. It is effective in situations where more complicated states exist and multiple operations need to appear as a single atomic action.

Combining volatile and synchronized

Though often used separately, there are use cases where both volatile and synchronized might be used together. For instance, using volatile for read-heavy shared variables along with synchronization to ensure atomic updates can leverage the strengths of both mechanisms.

Conclusion

Understanding the difference between volatile and synchronized helps in making informed decisions when designing multithreaded applications. While volatile is lightweight and easy to implement, synchronized offers comprehensive control over critical sections. Choosing the right mechanism depends largely on the specific needs of the application, making it essential to comprehend both their capabilities and limitations.


Course illustration
Course illustration

All Rights Reserved.