The volatile modifier has always been an interesting and tricky topic for many Java programmers. I still feel that it's one of the most underutilized modifiers in Java, which can do a lot of good if understood and applied correctly, after all, it provides a lock-free way to achieve synchronization in Java. If a field is shared between multiple threads and one of them changes its value i.e. one thread reads from the field which is written by other threads, then, by using a volatile modifier, you can synchronize access to this field. The volatile modifier in Java provides visibility and ordering guarantees without any locking.
You might know that compiler and JVM can re-order your code due to various reasons like performance improvement which can be a problem in concurrent Java applications. By making a filed volatile in Java you can instruct the compiler, JVM, and JIT to doesn't reorder them preventing subtle and hard-to-find multi-threading bugs.
Similarly, the visibility guarantee ensures that memory barriers are refreshed when a volatile variable is read, hence all the changes made by one thread before writing into a volatile variable is visible to the other thread that read from a volatile variable, this is also a part of "happens-before" guarantee provided by volatile variables.
Though, you need to be a little bit careful because volatile doesn't provide atomicity and mutual exclusive access, which is one of the key differences between synchronized and volatile keywords in Java.
You might know that compiler and JVM can re-order your code due to various reasons like performance improvement which can be a problem in concurrent Java applications. By making a filed volatile in Java you can instruct the compiler, JVM, and JIT to doesn't reorder them preventing subtle and hard-to-find multi-threading bugs.
Similarly, the visibility guarantee ensures that memory barriers are refreshed when a volatile variable is read, hence all the changes made by one thread before writing into a volatile variable is visible to the other thread that read from a volatile variable, this is also a part of "happens-before" guarantee provided by volatile variables.
Though, you need to be a little bit careful because volatile doesn't provide atomicity and mutual exclusive access, which is one of the key differences between synchronized and volatile keywords in Java.
Hence, the volatile variable should only be used in the assignment is the only operation performed on them.
In this article, I am going to 10 important points about volatile modifiers in a field that will help you to learn this useful concept better in the Java multi-threading world.
And, if you are serious about mastering Java multi-threading and concurrency then I also suggest you take a look at the Java Multithreading, Concurrency, and Performance Optimization course by Michael Pogrebinsky on Udemy. This is an advanced course to become an expert in Multithreading, concurrency, and Parallel programming in Java with a strong emphasis on high performance.
1) The volatile modifier provides lock-free synchronization of fields. Unlike synchronized keyword, when a thread read from the volatile variable or write into the volatile field, no lock is acquired or released.
2) The volatile modifier can only be applied to a field. You cannot apply the volatile keyword with methods and local variables. Of course, you don't need any synchronization for local variables because they are not shared between multiple threads. Every thread has its own copy of local variables.
3) When you make a field volatile in Java, it signals the compiler and Java virtual machine that this field may be concurrently updated by other threads. Due to this reason, the compiler stops re-ordering instructions for maximum throughput involving the volatile field.
4) Apart from ordering, a volatile modifier also provides a visibility guarantee. Any change made to the volatile variable is visible to all threads. The value of the volatile variable is not cached by threads, instead, they are always read from the main memory.
5) The volatile modifier also provides the happens-before guarantee, A write to volatile variable happens before any subsequent read. It also causes the memory barrier to be flushed, which means all changes made by thread A before writing into the volatile variable will be visible to thread B when it read the value of the volatile field.
In this article, I am going to 10 important points about volatile modifiers in a field that will help you to learn this useful concept better in the Java multi-threading world.
And, if you are serious about mastering Java multi-threading and concurrency then I also suggest you take a look at the Java Multithreading, Concurrency, and Performance Optimization course by Michael Pogrebinsky on Udemy. This is an advanced course to become an expert in Multithreading, concurrency, and Parallel programming in Java with a strong emphasis on high performance.
What is a volatile variable in Java? Example
Here are a couple of useful points a Java developer should know about volatile modifiers. Since many of you might not have the first-hand experience of using volatile variables in your application, these points will help you to understand the existing code written by some experienced programmers and which uses the volatile modifier for synchronization and inter-thread communication.1) The volatile modifier provides lock-free synchronization of fields. Unlike synchronized keyword, when a thread read from the volatile variable or write into the volatile field, no lock is acquired or released.
2) The volatile modifier can only be applied to a field. You cannot apply the volatile keyword with methods and local variables. Of course, you don't need any synchronization for local variables because they are not shared between multiple threads. Every thread has its own copy of local variables.
3) When you make a field volatile in Java, it signals the compiler and Java virtual machine that this field may be concurrently updated by other threads. Due to this reason, the compiler stops re-ordering instructions for maximum throughput involving the volatile field.
4) Apart from ordering, a volatile modifier also provides a visibility guarantee. Any change made to the volatile variable is visible to all threads. The value of the volatile variable is not cached by threads, instead, they are always read from the main memory.
5) The volatile modifier also provides the happens-before guarantee, A write to volatile variable happens before any subsequent read. It also causes the memory barrier to be flushed, which means all changes made by thread A before writing into the volatile variable will be visible to thread B when it read the value of the volatile field.
Several high-performance concurrency frameworks like LMAX Disruptor utilizes this property of volatile variables to achieve lock-free synchronization.
If you want to know more about "happens-before" rules, please read Java Concurrency in Practice by Brian Goetz, it has a nice list of all the actions covered under this rule and how they work under the Java Memory Model.
6) The volatile modifier provides a low-cost synchronization alternative of synchronized keyword, albeit it doesn't replace synchronized block or synchronized method because it doesn't provide atomicity or mutual exclusion. The low cost is because it's lock-free. Threads are not blocked for lock and they don't spend time on acquiring and releasing the lock.
7) When to use the volatile variable is the most common question from many Java developers, even experienced developers ask this question. The reason being is the lack of opportunity to write concurrent code which makes use of the volatile variable.
Well, one of the most common uses of the volatile variable is the shared boolean flag. It is also one of the most popular Java concurrency interview questions for senior developers
Suppose an object has a boolean flag, bExit, which is set by one thread and queries by another thread. In the classical game loop scenario, a user can press the exit button to set the value of bExit to true to stop the game.
Suppose an object has a boolean flag, bExit, which is set by one thread and queries by another thread. In the classical game loop scenario, a user can press the exit button to set the value of bExit to true to stop the game.
Here the thread which will set the bExit = true will be the event listener thread and the thread which will read this value will be your game thread.
In this case, it's reasonable to declare the bExit field as volatile as shown below
Another common use of the volatile field is in a double-checked locking pattern for implementing thread-safe Singleton in Java. If you don't use volatile on a shared field that it's possible that the game thread will never see the change made by the event listener thread.
8) Always remember, volatile fields do not provide any atomicity guarantee. For example, you cannot make a counter volatile and assume that i++ will be atomic. Similarly, flipping a volatile boolean variable is not atomic
If you need atomicity, you should use Atomic classes from the java.util.concurrent.atomic package e.g. AtomicInteger can be used as a concurrent shared counter or you can use the plain old synchronized keyword to make compound statement atomic.
In this case, it's reasonable to declare the bExit field as volatile as shown below
private volatile boolean bExit; public boolean isExit() { return bExit; } public void setExit(boolean exit){ this.bExit = exit; }
Another common use of the volatile field is in a double-checked locking pattern for implementing thread-safe Singleton in Java. If you don't use volatile on a shared field that it's possible that the game thread will never see the change made by the event listener thread.
8) Always remember, volatile fields do not provide any atomicity guarantee. For example, you cannot make a counter volatile and assume that i++ will be atomic. Similarly, flipping a volatile boolean variable is not atomic
public void flip() { exit = !exit ; // not atomic };
If you need atomicity, you should use Atomic classes from the java.util.concurrent.atomic package e.g. AtomicInteger can be used as a concurrent shared counter or you can use the plain old synchronized keyword to make compound statement atomic.
If you have trouble understanding concurrency fundamentals like locking, synchronization, mutual exclusion, atomicity, I strongly suggest first read an introductory book in threading like Java Threads: Understanding and Mastering Concurrent Programming by Scott Oaks.
9) You should only make a variable volatile if you perform an atomic operation on them e.g. assignment. Assigning a value to a boolean or int variable are atomic but reading and writing long and double is not atomic unless they are volatile. So, one more use of volatile keywords is to make the long and double assignment atomic in Java.
10) The key difference between volatile and synchronized modifier is locking, the synchronized keyword need a lock but volatile is lock-free. Due to this reason, the cost of synchronization is also less in the case of the volatile variable.
The second significant difference between synchronized and volatile modifier is atomicity, the synchronized keyword provides the atomic guarantee and can make a block of code atomic but the volatile variable doesn't provide such a guarantee, except the case discussed in the last case of making long and double read atomic in Java.
That's all about the volatile modifier in Java. There is a lot to learn as writing a concurrent application is not easy in Java but you can use these points to refresh your knowledge and understand the concept better.
9) You should only make a variable volatile if you perform an atomic operation on them e.g. assignment. Assigning a value to a boolean or int variable are atomic but reading and writing long and double is not atomic unless they are volatile. So, one more use of volatile keywords is to make the long and double assignment atomic in Java.
10) The key difference between volatile and synchronized modifier is locking, the synchronized keyword need a lock but volatile is lock-free. Due to this reason, the cost of synchronization is also less in the case of the volatile variable.
The second significant difference between synchronized and volatile modifier is atomicity, the synchronized keyword provides the atomic guarantee and can make a block of code atomic but the volatile variable doesn't provide such a guarantee, except the case discussed in the last case of making long and double read atomic in Java.
That's all about the volatile modifier in Java. There is a lot to learn as writing a concurrent application is not easy in Java but you can use these points to refresh your knowledge and understand the concept better.
Use volatile modifier if you just need to synchronize access to a shared variable whose value is set by one thread and queried by others. It provides a low-cost alternative to the synchronized keyword or lock interface introduced in Java 5 without atomicity and mutual exclusion.
Other Java tutorials you may like to explore:
Other Java tutorials you may like to explore:
- 10 points about Thread in Java (read)
- 10 points about Java Heap space or heap memory (read)
- 10 points about the wait, notify, and notifyAll in Java (see)
- 10 points about static modifier in Java (see)
- How thread works in Java? (example)
- 10 points about instanceof operator in Java (read)
- 10 points about Enum in Java (read)
- 10 points about the finalize method in Java (tutorial)
- 10 points about java.lang.String class in Java (tutorial)
- 10 Courses to learn Java Programming in-depth (Java courses)
- 10 Free Courses to learn Java for beginners (Free courses)
- My favorite Multithreading Courses in Java (concurrency courses)
And lastly, one question for you? what is difference between volatile and atomic variable in Java? Can you replace volatile variable with Atomic?
1 comment :
If I have to choose one of the worth less feature of Java then I would name "volatile" variable, I have never used it neither I found anyone who have used in this production. It's neither here nor there, you are much safer when you use synchronized or parallel stream
Post a Comment