Java provides two important keywords, volatile
and synchronized
, which play a crucial role in multi-threaded programming. These keywords help in ensuring visibility and atomicity, respectively, when dealing with shared variables and concurrent access from multiple threads.
The volatile
keyword is used to indicate that a variable may be modified by multiple threads and therefore needs to be accessed directly from the main memory. It guarantees that changes made to the variable by one thread are immediately visible to other threads.
When a variable is declared as volatile, the JVM ensures that changes made by one thread are not stored in a cache, but in the main memory directly. Other threads reading the value will always get the most up-to-date value from the main memory instead of relying on a cached value.
public class VolatileExample {
private volatile boolean isRunning = true;
public void stop() {
isRunning = false;
}
public void start() {
while (isRunning) {
// perform some task
}
}
}
In the example above, the isRunning
variable is declared as volatile. This guarantees that any change made to it by another thread will be immediately visible within the start()
method. Without the volatile
keyword, the start()
method might cache the value of isRunning
and never exit the loop, even if stop()
is called from another thread.
The synchronized
keyword is used to create a mutually exclusive block of code known as a synchronized block. It ensures that only one thread can enter the synchronized block at a time, preventing multiple threads from simultaneously modifying the same shared variables, thereby avoiding data inconsistencies.
There are two ways to use the synchronized
keyword:
Synchronized block: We can define a block of code to be executed exclusively by one thread at a time.
public class SynchronizedExample {
private int count = 0;
public void increment() {
synchronized (this) {
count++;
}
}
}
In the example above, the increment()
method is synchronized on the instance of the SynchronizedExample
class itself. Only one thread will be able to access and modify the count
variable at a time.
Synchronized method: We can define an entire method to be executed exclusively by one thread at a time.
public synchronized void increment() {
count++;
}
In this approach, the entire increment()
method is synchronized, and only one thread can execute it at a time. The synchronization is automatically applied to the instance of the object on which the method is called.
Synchronization can also be applied to static methods or blocks to achieve mutually exclusive access among threads operating on static variables.
While volatile
ensures visibility of variables across threads and prevents caching, it does not provide atomicity. On the other hand, synchronized
ensures both visibility and atomicity, as it allows only one thread to execute the synchronized code block at a time.
It is important to note that the overuse of synchronized
can lead to performance degradation, as it introduces thread contention. Therefore, volatile
is usually preferred when there is a single simple variable that requires thread visibility, while synchronized
is useful for more complex scenarios that involve multiple variables or operations.
In summary, volatile
is used when variables are simple and independent, while synchronized
is used when we need to maintain consistency or synchronization between multiple threads accessing shared resources.
The volatile
and synchronized
keywords are powerful tools in Java to handle multi-threaded programming challenges. They help in maintaining thread visibility and synchronization, ensuring the correct execution and consistency of concurrent programs. Understanding their differences and when to apply each one is crucial for writing efficient and thread-safe code.
noob to master © copyleft