package basic; public class TestVolatile { public volatile static int count = 0; public static void increase() { try { // 延迟10毫秒,使得结果明显 Thread.sleep(10); count++; } catch (InterruptedException e) { e.printStackTrace(); } } public static void main(String[] args) { for (int i = 0; i < 10000; i++) { new Thread(new Runnable() { @Override public void run() { TestVolatile.increase(); } }).start(); } System.out.println("期望运行结果:10000"); System.out.println("实际运行结果:" + TestVolatile.count); } }
代码如下,由于volatile的read and load操作并不是原子性的,它只保证了从主内存读到栈内存的值是最新的,因此上述代码运行结果并不是语气的10000。
问题:
1.既然volatile并没有保证原子性,为什么还要使用它?
2.JDK中针对volatile的这种弊端,还做了哪些设计来弥补这一缺憾?
volatile
It can avoid register optimization, and Java also comes with a barrier. Although it cannot prevent competition, it can be used for competition-free synchronization across threads.If there is still competition for data, you should consider the Atomic data type. However, it is best to avoid competition as much as possible, because competition will reduce concurrency.
@FreeBirdLjj has already summarized the general function of
volatile
, and I would like to add a few more pointsvolatile
The function is to ensure the memory visibility of variables between different threads and the atomicity of reading and writing to a single volatile variable. The main method is to insert a specific type of memory barrier when the compiler generates an instruction sequence.volatile
The visibility of memory is mainly achieved by prohibiting reordering (compiler reordering, processor reordering), ensuring that the cache expires in time, and ensuring that the cache is flushed into the main memory in a timely mannerThe semantics of reading and setting a single
volatile
variable are equivalent topublic synchronized A getA();
public synchronized void setA();
During the execution of a task, in order to improve efficiency, the initialization process is sometimes reordered, which may make the program unstable in the case of multi-thread concurrency. Use volatile to prevent reordering.
Between 2 and 3 in the above three lines of pseudocode, may be reordered (on some JIT compilers, this reordering actually occurs, see "Out-of-order writes" in Reference 1 for details part). The execution timing after reordering between 2 and 3 is as follows:
If multiple threads access concurrently at this time, the object may not be initialized yet.
The volatile keyword will prevent this optimization and ensure that it will be executed in the order of the first piece of code every time.
Source: http://www.infoq.com/cn/articles/double-checked-locking-with-delay-initialization
What you need is [Lock].