I am trying to understand how multithreading works in Java. I understand the difference between Volatile
and Synchronization
.
Volatile
is about visibility and doesn't guarantee synchronization. When we are working with multithreading environments, each thread creates its own copy on a local cache of the variable they are dealing with. When this value is being updated, the update happens first in the local cache copy, and not in the real variable. Therefore, other threads are agnostic about the values that other threads are changing. And this is where volatile
comes into picture. Volatile fields are immediately written through to main memory, and reads occur from main memory.
Snippet from Thinking In Java
-
Synchronization also causes flushing to main memory, so if a field is completely guarded by synchronized methods or blocks, it is not necessary to make it volatile.
It’s typically only safe to use volatile instead of synchronized if the class has only one mutable field. Again, your first choice should be to use the synchronized keyword—that’s the safest approach, and trying to do anything else is risky.
But my question is, if in a synchronized block, a non volatile shared variable is being modified, will the other threads see the updated data ? (Since the variable in question is non volatile, other threads should read stale data from cache instead of main main memory)
If the answer to the above question is NO
, then can I conclude that everytime I use synchronization, I should ensure that shared variables must be marked volatile
?
And if the answer is YES
, then does that mean that I can always use synchronization
instead of marking shared variables are volatile
?
p.s: Before asking this question, I have read through lot of answers on StackOverflow and on other sites, but I couldn't find the answer to my question.
A variable should only be marked volatile if it is used both inside an ISR, and outside one. Variables only used outside an ISR should not be volatile. Variables only used inside an ISR should not be volatile.
Using volatile is yet another way (like synchronized, atomic wrapper) of making class thread-safe. Thread-safe means that a method or class instance can be used by multiple threads at the same time without any problem.
If you write volatile variable from multiple threads without using any synchronized constructs, you are bound to get data inconsistency errors. Use volatile variables without synchronization in case of single write thread and multiple read threads for atomic operations.
If it is accessed only from synchronized blocks is not needed the volatile keyword. Synchronized guarantees that changes to variables accessed inside the synchronized block are visible to all threads entering a synchronized block.
To simplify a little:
volatile
only provides visibility: when you read a volatile
variable you get two guarantees: (1) you see the latest write to the variable, even if it was performed in another thread and (2) all the writes before that volatile
write are also visible.synchronized
gives you visibility AND atomicity - thread observing the actions performed in a synchronized
block from a synchronized
block using the same monitor will either see all of them or none of them.So to answer your question, no, if a variable is written to within a synchronized
block, you don't need to mark it volatile
, provided that you always read that variable from a synchronized
block using the same monitor.
Here are a few examples with volatile:
static class TestVolatile {
private int i = 0;
private volatile int v = 0;
void write() {
i = 5;
v = 7;
}
void read() {
//assuming write was called beforehand
print(i); //could be 0 or 5
print(v); //must be 7
print(i); //must be 5
}
void increment() {
i = i + 1; //if two threads call the method concurrently
//i could be incremented by 1 only, not 2: no atomicity
}
}
And a few examples with synchronized
:
static class TestSynchronized {
private int i = 0;
private int j = 0;
void write() {
synchronized(this) {
i = 5;
j = 7;
}
}
void read_OK() {
synchronized(this) {
//assuming write was called beforehand
print(i); //must be 5
print(j); //must be 7
print(i); //must be 5
}
}
void read_NOT_OK() {
synchronized(new Object()) { //not the same monitor
//assuming write was called beforehand
print(i); //can be 0 or 5
print(j); //can be 0 or 7
}
}
void increment() {
synchronized(this) {
i = i + 1; //atomicity guarantees that if two threads call the method
//concurrently, i will be incremented twice
}
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With