Java Volatile Keyword
2016-02-02 19:22
555 查看
转自:
http://tutorials.jenkov.com/java-concurrency/volatile.html
The Java
write to a volatile variable will be written to main memory, and not just to the CPU cache.
Actually, since Java 5 the
The Java
In a multithreaded application where the threads operate on non-volatile variables, each thread may copy variables from main memory into a CPU cache while working on them, for performance reasons. If your computer contains more than one CPU, each thread may
run on a different CPU. That means, that each thread may copy the variables into the CPU cache of different CPUs. This is illustrated here:
With non-volatile variables there are no guarantees about when the Java Virtual Machine (JVM) reads data from main memory into CPU caches, or writes data from CPU caches to main memory. This can cause several problems which I will explain in the following sections.
Imagine a situation in which two or more threads have access to a shared object which contains a counter variable declared like this:
public class SharedObject {
public int counter = 0;
}
Imagine too, that only Thread 1 increments the
If the
in the CPU cache may not be the same as in main memory. This situation is illustrated here:
The problem with threads not seeing the latest value of a variable because it has not yet been written back to main memory by another thread, is called a "visibility" problem. The updates of one thread are not visible to other threads.
By declaring the
Here is how the
Declaring a variable
Since Java 5 the
If Thread A writes to a volatile variable and Thread B subsequently reads the same volatile variable, then all variables visible to Thread A before writing the volatile variable, will also be visible to Thread B after it has read the volatile
variable.
The reading and writing instructions of volatile variables cannot be reordered by the JVM (the JVM may reorder instructions for performance reasons as long as the JVM detects no change in program behaviour from the reordering). Instructions before and after
can be reordered, but the volatile read or write cannot be mixed with these instructions. Whatever instructions follow a read or write of a volatile variable are guaranteed to happen after the read or write.
These statements require a deeper explanation.
When a thread writes to a volatile variable, then not just the volatile variable itself is written to main memory. Also all other variables changed by the thread before writing to the volatile variable are also flushed to main memory. When a thread reads a
volatile variable it will also read all other variables from main memory which were flushed to main memory together with the volatile variable.
Look at this example:
Since Thread A writes the non-volatile variable
memory when Thread A writes to
Since Thread B starts by reading the volatile
will see the value written by Thread A.
Developers may use this extended visibility guarantee to optimize the visibility of variables between threads. Instead of declaring each and every variable
a simple
Thread A may be putting objects from time to time by calling
of
However, the JVM may reorder Java instructions to optimize performance, if the JVM can do so without changing the semantics of the reordered instructions. What would happen if the JVM switched the order of the reads and writes inside
What if
Notice the write to the
However, reordering the instruction execution would harm the visibility of the
Second, there is now not even a guarantee about when the new value written to
To prevent situations like the one described above from occurring, the
cannot be reordered. Instructions before and after can be reordered, but the volatile read/write instruction cannot be reordered with any instruction occurring before or after it.
Look at this example:
The JVM may reorder the first 3 instructions, as long as all of them happens before the
Similarly, the JVM may reorder the last 3 instructions as long as the volatile write instruction happens before all of them. None of the last 3 instructions can be reordered to before the volatile write instruction.
That is basically the meaning of the Java volatile happens before guarantee.
Even if the
it is not enough to declare a variable
In the situation explained earlier where only Thread 1 writes to the shared
In fact, multiple threads could even be writing to a shared
a value to the shared
As soon as a thread needs to first read the value of a
The short time gap in between the reading of the
condition where multiple threads might read the same value of the
The situation where multiple threads are incrementing the same counter is exactly such a situation where a
Imagine if Thread 1 reads a shared
the value of the variable is still 0, into its own CPU cache. Thread 2 could then also increment the counter to 1, and also not write it back to main memory. This situation is illustrated in the diagram below:
Thread 1 and Thread 2 are now practically out of sync. The real value of the shared
should have been 2, but each of the threads has the value 1 for the variable in their CPU caches, and in main memory the value is still 0. It is a mess! Even if the threads eventually write their value for the shared
back to main memory, the value will be wrong.
As I have mentioned earlier, if two threads are both reading and writing to a shared variable, then using the
that case to guarantee that the reading and writing of the variable is atomic. Reading or writing a volatile variable does not block threads reading or writing. For this to happen you must use the
As an alternative to a
For instance, the
one of the others.
In case only one thread reads and writes the value of a volatile variable and other threads only read the variable, then the reading threads are guaranteed to see the latest value written to the volatile variable. Without making the variable volatile, this
would not be guaranteed.
The
Reading and writing of volatile variables causes the variable to be read or written to main memory. Reading from and writing to main memory is more expensive than accessing the CPU cache. Accessing volatile variables also prevent instruction reordering which
is a normal performance enhancement technique. Thus, you should only use volatile variables when you really need to enforce visibility of variables.
http://tutorials.jenkov.com/java-concurrency/volatile.html
The Java
volatilekeyword is used to mark a Java variable as "being stored in main memory". More precisely that means, that every read of a volatile variable will be read from the computer's main memory, and not from the CPU cache, and that every
write to a volatile variable will be written to main memory, and not just to the CPU cache.
Actually, since Java 5 the
volatilekeyword guarantees more than just that volatile variables are written to and read from main memory. I will explain that in the following sections.
The Java volatile Visibility Guarantee
The Java volatilekeyword guarantees visibility of changes to variables across threads. This may sound a bit abstract, so let me elaborate.
In a multithreaded application where the threads operate on non-volatile variables, each thread may copy variables from main memory into a CPU cache while working on them, for performance reasons. If your computer contains more than one CPU, each thread may
run on a different CPU. That means, that each thread may copy the variables into the CPU cache of different CPUs. This is illustrated here:
With non-volatile variables there are no guarantees about when the Java Virtual Machine (JVM) reads data from main memory into CPU caches, or writes data from CPU caches to main memory. This can cause several problems which I will explain in the following sections.
Imagine a situation in which two or more threads have access to a shared object which contains a counter variable declared like this:
public class SharedObject {
public int counter = 0;
}
Imagine too, that only Thread 1 increments the
countervariable, but both Thread 1 and Thread 2 may read the
countervariable from time to time.
If the
countervariable is not declared
volatilethere is no guarantee about when the value of the
countervariable is written from the CPU cache back to main memory. This means, that the
countervariable value
in the CPU cache may not be the same as in main memory. This situation is illustrated here:
The problem with threads not seeing the latest value of a variable because it has not yet been written back to main memory by another thread, is called a "visibility" problem. The updates of one thread are not visible to other threads.
By declaring the
countervariable
volatileall writes to the
countervariable will be written back to main memory immediately. Also, all reads of the
countervariable will be read directly from main memory.
Here is how the
volatiledeclaration of the
countervariable looks:
public class SharedObject { public volatile int counter = 0; }
Declaring a variable
volatilethus guarantees the visibility for other threads of writes to that variable.
The Java volatile Happens-Before Guarantee
Since Java 5 the volatilekeyword guarantees more than just the reading from and writing to main memory of variables. Actually, the
volatilekeyword guarantees this:
If Thread A writes to a volatile variable and Thread B subsequently reads the same volatile variable, then all variables visible to Thread A before writing the volatile variable, will also be visible to Thread B after it has read the volatile
variable.
The reading and writing instructions of volatile variables cannot be reordered by the JVM (the JVM may reorder instructions for performance reasons as long as the JVM detects no change in program behaviour from the reordering). Instructions before and after
can be reordered, but the volatile read or write cannot be mixed with these instructions. Whatever instructions follow a read or write of a volatile variable are guaranteed to happen after the read or write.
These statements require a deeper explanation.
When a thread writes to a volatile variable, then not just the volatile variable itself is written to main memory. Also all other variables changed by the thread before writing to the volatile variable are also flushed to main memory. When a thread reads a
volatile variable it will also read all other variables from main memory which were flushed to main memory together with the volatile variable.
Look at this example:
Thread A: sharedObject.nonVolatile = 123; sharedObject.counter = sharedObject.counter + 1; Thread B: int counter = sharedObject.counter; int nonVolatile = sharedObject.nonVolatile;
Since Thread A writes the non-volatile variable
sharedObject.nonVolatilebefore writing to the volatile
sharedObject.counter, then both
sharedObject.nonVolatileand
sharedObject.counterare written to main
memory when Thread A writes to
sharedObject.counter(the
volatilevariable).
Since Thread B starts by reading the volatile
sharedObject.counter, then both the
sharedObject.counterand
sharedObject.nonVolatileare read from main memory into the CPU cache used by Thread B. By the time Thread B reads
sharedObject.nonVolatileit
will see the value written by Thread A.
Developers may use this extended visibility guarantee to optimize the visibility of variables between threads. Instead of declaring each and every variable
volatile, only one or a few need be declared
volatile. Here is an example of
a simple
Exchangerclass written after that principle:
public class Exchanger { private Object object = null; private volatile hasNewObject = false; public void put(Object newObject) { while(hasNewObject) { //wait - do not overwrite existing new object } object = newObject; hasNewObject = true; //volatile write } public Object take(){ while(!hasNewObject){ //volatile read //wait - don't take old object (or null) } Object obj = object; hasNewObject = false; //volatile write return obj; } }
Thread A may be putting objects from time to time by calling
put(). Thread B may take objects from time to time by calling
take(). This
Exchangercan work just fine using a
volatilevariable (without the use
of
synchronizedblocks), as long as only Thread A calls
put()and only Thread B calls
take().
However, the JVM may reorder Java instructions to optimize performance, if the JVM can do so without changing the semantics of the reordered instructions. What would happen if the JVM switched the order of the reads and writes inside
put()and
take()?
What if
put()was really executed like this:
while(hasNewObject) { //wait - do not overwrite existing new object } hasNewObject = true; //volatile write object = newObject;
Notice the write to the
volatilevariable
hasNewObjectis now executed before the new object is actually set. To the JVM this may look completely valid. The values of the two write instructions do not depend on each other.
However, reordering the instruction execution would harm the visibility of the
objectvariable. First of all, Thread B might see
hasNewObjectset to
truebefore Thread A has actually written a new value to the
objectvariable.
Second, there is now not even a guarantee about when the new value written to
objectwill be flushed back to main memory (well - the next time Thread A writes to a volatile variable somewhere...).
To prevent situations like the one described above from occurring, the
volatilekeyword comes with a "happens before guarantee". The happens before guarantee guarantees that read and write instructions of
volatilevariables
cannot be reordered. Instructions before and after can be reordered, but the volatile read/write instruction cannot be reordered with any instruction occurring before or after it.
Look at this example:
sharedObject.nonVolatile1 = 123; sharedObject.nonVolatile2 = 456; sharedObject.nonVolatile3 = 789; sharedObject.volatile = true; //a volatile variable int someValue1 = sharedObject.nonVolatile4; int someValue2 = sharedObject.nonVolatile5; int someValue3 = sharedObject.nonVolatile6;
The JVM may reorder the first 3 instructions, as long as all of them happens before the
volatilewrite instruction (they must all be executed before the volatile write instruction).
Similarly, the JVM may reorder the last 3 instructions as long as the volatile write instruction happens before all of them. None of the last 3 instructions can be reordered to before the volatile write instruction.
That is basically the meaning of the Java volatile happens before guarantee.
volatile is Not Always Enough
Even if the volatilekeyword guarantees that all reads of a
volatilevariable are read directly from main memory, and all writes to a
volatilevariable are written directly to main memory, there are still situations where
it is not enough to declare a variable
volatile.
In the situation explained earlier where only Thread 1 writes to the shared
countervariable, declaring the
countervariable
volatileis enough to make sure that Thread 2 always sees the latest written value.
In fact, multiple threads could even be writing to a shared
volatilevariable, and still have the correct value stored in main memory, if the new value written to the variable does not depend on its previous value. In other words, if a thread writing
a value to the shared
volatilevariable does not first need to read its value to figure out its next value.
As soon as a thread needs to first read the value of a
volatilevariable, and based on that value generate a new value for the shared
volatilevariable, a
volatilevariable is no longer enough to guarantee correct visibility.
The short time gap in between the reading of the
volatilevariable and the writing of its new value, creates an race
condition where multiple threads might read the same value of the
volatilevariable, generate a new value for the variable, and when writing the value back to main memory - overwrite each other's values.
The situation where multiple threads are incrementing the same counter is exactly such a situation where a
volatilevariable is not enough. The following sections explain this case in more detail.
Imagine if Thread 1 reads a shared
countervariable with the value 0 into its CPU cache, increment it to 1 and not write the changed value back into main memory. Thread 2 could then read the same
countervariable from main memory where
the value of the variable is still 0, into its own CPU cache. Thread 2 could then also increment the counter to 1, and also not write it back to main memory. This situation is illustrated in the diagram below:
Thread 1 and Thread 2 are now practically out of sync. The real value of the shared
countervariable
should have been 2, but each of the threads has the value 1 for the variable in their CPU caches, and in main memory the value is still 0. It is a mess! Even if the threads eventually write their value for the shared
countervariable
back to main memory, the value will be wrong.
When is volatile Enough?
As I have mentioned earlier, if two threads are both reading and writing to a shared variable, then using the volatilekeyword for that is not enough. You need to use a synchronized in
that case to guarantee that the reading and writing of the variable is atomic. Reading or writing a volatile variable does not block threads reading or writing. For this to happen you must use the
synchronizedkeyword around critical sections.
As an alternative to a
synchronizedblock you could also use one of the many atomic data types found in the
java.util.concurrentpackage.
For instance, the
AtomicLongor
AtomicReferenceor
one of the others.
In case only one thread reads and writes the value of a volatile variable and other threads only read the variable, then the reading threads are guaranteed to see the latest value written to the volatile variable. Without making the variable volatile, this
would not be guaranteed.
The
volatilekeyword is guaranteed to work on 32 bit and 64 variables.
Performance Considerations of volatile
Reading and writing of volatile variables causes the variable to be read or written to main memory. Reading from and writing to main memory is more expensive than accessing the CPU cache. Accessing volatile variables also prevent instruction reordering whichis a normal performance enhancement technique. Thus, you should only use volatile variables when you really need to enforce visibility of variables.
相关文章推荐
- Java集合中对象排序
- springmvc日期转换两种方式
- Java基础:关键字(保留字)
- java语言建立顺序表
- spring整合junit集成测试
- eclipse代码自动补全[转]
- Java编程中“为了性能”需做的26件事
- 关于eclipse的jvm设置
- java替换pdf模板出现中文乱码问题
- SpringMVC视图解析器
- java web 获取客户端真实IP
- java1.8的几大新特性(二)
- eclipse内存溢出解决
- 编写各种outofmemory/stackoverflow程序
- 11.Java 加解密技术系列之 总结
- SpringMVC4 + Spring + MyBatis3
- 10.Java 加解密技术系列之 DH
- 第十二章 与Spring集成——《跟我学Shiro》
- eclipse常用快捷键
- java30:设计模式