Volatile 关键字告诉编译器不要持有变量的临时性拷贝。一般用在多线程程序中,以避免在其中一个线程操作该变量时,将其拷贝入寄存器。请看以下情形:
在此种情况下,当B线程改变了变量的值时,已改变的值对其在寄存器的值没有影响。所以A线程进入死循环。
What does volatile do?
This is probably best explained by comparing the effects that
volatile and
synchronized have on a
method. 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. Of course, it is likely that
volatile variables have a
higher access and update overhead than "plain" variables, since the
reason threads can have their own copy of data is for better
efficiency.
Well if volatile already
synchronizes data across threads, what is synchronized for? Well there are two
differences. Firstly synchronized obtains and releases locks on
monitors which can force only one thread at a time to execute a
code block, if both threads use the same monitor (effectively the
same object lock). 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(assuming the monitor is unlocked, otherwise the thread waits until the monitor is unlocked). - The thread memory flushes all its variables, i.e. it has all of its variables effectively read from "main" memory (JVMs can use dirty sets to optimize this so that only "dirty" variables are flushed, but conceptually this is the same. See section 17.9 of the Java language specification).
- 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.
插入表情