Volatile keyword in Java - Clarification [duplicate]
I am really confused about what I read about the applications of volatile keyword in java.
Is the following statement correct? "a write to a volatile field happens before every subsequent read of the same field"
Ideally when should volatile keyword used?
What is the difference between:
class TestClass { private int x; synchronized int get(){return x;} synchronized void set(int x){this.x = x;} }
and
class TestClass
{ private volatile int x;
int get(){return x;}
void set(int x){this.x = x;}
}
volatile is a field modifier, while synchronized modifies code blocks and methods. So we can specify three variations of a simple accessor using those two keywords:
int i1;
int geti1() {return i1;}
volatile int i2;
int geti2() {return i2;}
int i3;
synchronized int geti3() {return i3;}
geti1()
accesses the value currently stored in i1
in the current thread.
Threads can have local copies of variables, and the data does not have to be the same as the data held in other threads.In particular, another thread may have updated i1
in it's thread, but the value in the current thread could be different from that updated value. In fact Java has the idea of a "main" memory, and this is the memory that holds the current "correct" value for variables. Threads can have their own copy of data for variables, and the thread copy can be different from the "main" memory. So in fact, it is possible for the "main" memory to have a value of 1 for i1
, for thread1 to have a value of 2 for i1
and for thread2 to have a value of 3 for i1
if thread1 and thread2 have both updated i1 but those updated value has not yet been propagated to "main" memory or other threads.
On the other hand, geti2()
effectively accesses the value of i2
from "main" memory. A volatile variable is not allowed to have a local copy of a variable that is different from the value currently held in "main" memory. Effectively, a variable declared volatile must have it's data synchronized across all threads, so that whenever you access or update the variable in any thread, all other threads immediately see the same value. Generally volatile variables have a higher access and update overhead than "plain" variables. Generally threads are allowed to have their own copy of data is for better efficiency.
There are two differences between volitile and synchronized.
Firstly synchronized obtains and releases locks on monitors which can force only one thread at a time to execute a code block. That's the fairly well known aspect to synchronized. But synchronized also synchronizes memory. In fact synchronized synchronizes the whole of thread memory with "main" memory. So executing geti3()
does the following:
- The thread acquires the lock on the monitor for object this .
- The thread memory flushes all its variables, i.e. it has all of its variables effectively read from "main" memory .
- The code block is executed (in this case setting the return value to the current value of i3, which may have just been reset from "main" memory).
- (Any changes to variables would normally now be written out to "main" memory, but for geti3() we have no changes.)
- The thread releases the lock on the monitor for object this.
So where volatile only synchronizes the value of one variable between thread memory and "main" memory, synchronized synchronizes the value of all variables between thread memory and "main" memory, and locks and releases a monitor to boot. Clearly synchronized is likely to have more overhead than volatile.
http://javaexp.blogspot.com/2007/12/difference-between-volatile-and.html
volatile
guarantees that reads from the variable always reflects the most up to update value. The runtime can achieve this in various ways, including not caching or refreshing the cache when the value has changed.
bwawok eluded to it, but the volatile keyword isnt only for memory visibility. Before Java 1.5 was released the volatile keyword declared that the field will get the most recent value of the object by hitting main memory each time for reads and flushing for writes.
Today's volatile keyword syas two very important things:
- Dont worry about how but know that when reading a volatile field you will always have the most up to date value.
- A compiler cannot re order a volatile read/write as to maintain program order.
From a client point of view, a private volatile field is hidden from the public interface while synchronized methods are more visible.
To answer part 3 of your question, and partly part 2.
There is no functional difference between synchronized
and volatile
samples.
However, each has it's own drawbacks in terms of performance. In some cases volatile
performance may be really worse than just using synchronized
or other primitives from java.util.concurrent
. For discussion of this see -> Why aren't variables in Java volatile by default?.
Answer by Kerem Baydoğan is completely right. I just want to give an practical example about what volatile
keyword offers us.
First, we have a counter, smth like
public class Counter {
private int x;
public int getX() { return x; }
public void increment() { x++; }
}
And some Runnable tasks which increments the value of x
@Override
public void run() {
for (N) {
int oldValue = counter.getX();
counter.increment();
int new value = counter.getX();
}
}
}
With NO synchronization there is going to be interference between threads and simply is not going to work
the simplest way to solve this:
public class Counter {
private int x;
public synchronized int getX() { return x; }
public synchronized void increment() { x++; }
}
Actually in order to force the system to break, I do a Thread.sleep
before reading and writing x
, just imagine is a BD or a huge task to deal with.
Now, what is volatile
useful for? There are a lot of good articles over there: volatile article or this question
synchronizing the access to the common resource is not the answer but is a good choice to hold the flag to stop threads
I our prev. example, imagine we want to increment the variable up to 100, a simply way could be a volatile boolean
flag. Example:
private volatile boolean stop;
@Override
public void run() {
while(!stop) {
int oldValue = counter.getX();
if (oldValue == 100) {
stop = true;
} else {
counter.increment();
int new value = counter.getX();
}
}
}
This works fine, but, if you remove the volatile
keyword from the flag, it's possible to come across and infinite loop.
精彩评论