使用volatile关键字时,会有很多误用,volatile修饰的变量只是保证对线程的可见性,被volatile修饰的变量被修改时,被强制写入内存,所以其他的线程保证得到的变量值是最新的。现在我们看看我们疑惑的代码:
如以下代码:
<span style="font-size:14px;">public class Test { </span>
<span style="font-size:14px;"> volatile static int x = 0; </span>
<span style="font-size:14px;"> public static void main(String[] args) { </span>
<span style="font-size:14px;"><span style="white-space:pre"> </span> for (int i = 0; i < 1000; i++) { </span>
<span style="font-size:14px;"><span style="white-space:pre"> </span> new Thread(new Runnable() { </span>
<span style="font-size:14px;"><span style="white-space:pre"> </span> @Override </span>
<span style="font-size:14px;"><span style="white-space:pre"> </span>public void run() { </span>
<span style="font-size:14px;"><span style="white-space:pre"> </span>Test.add(); } </span>
<span style="font-size:14px;"><span style="white-space:pre"> </span> }).start(); </span>
<span style="font-size:14px;"><span style="white-space:pre"> </span> } </span>
<span style="font-size:14px;"><span style="white-space:pre"> </span> System.out.println(x); </span>
<span style="font-size:14px;"><span style="white-space:pre"> </span>} </span>
<span style="font-size:14px;"><span style="white-space:pre"> </span>public static void add() { x++; }</span>
<span style="font-size:14px;">}</span>
结果并不像我们想的会输出1000,而是输出了997;
我们来分析一下原因:
多线程并发时,虚拟机会为每一个线程创建一个自己的线程栈,线程栈保存了变量运行时的信息。
然后线程在运行时会找到变量的信息并且加载到自己的线程栈中,在栈中复制一份变量的副本,接着线程在运行时就不再访问堆中的变量,而是直接修改访问自己的线程栈中的变量。
有些人就会问既然volatile修饰的变量对线程可见,就说明每一次自增运算以后x都被强制写入内存中,说明下一个线程进行读操作时x发生改变,就可以进行正确的自增,那为什么上面的程序没有输出1000?
这是因为volatile虽然可以保证所有线程可见,但它不能保证操作的原子性:
怎样理解这句话呢?我们先来看看线程工作的流程图:
volatile可以保证在读操作时读到的值是最新的,现在考虑如下问题:
如果现在x=0;线程1读完操作和加载操作已经完成,而这时cpu不执行x线程1,转而执行线程2(cup调度时是以时间片来调度的),此时线程1还没有执行同步操作,因此没有写入主存,而线程2此时开始读主存,那一定读到的是x=0,此时县城2对变量操作,即x++,然后线程2中的x的变量副本变成了1,cpu调度线程1继续执行,此时线程1执行到了对x++此时x在线程1中的变量副本的值为1。
所以线程1,和线程2对x的值都进行了++操作,但是x的值只加了1。
这是因为volatile关键字不能保证线程操作的原子性。所以还是会出现并发不安全的现象。
参考: http://www.cnblogs.com/aigongsi/archive/2012/04/01/2429166.html
http://www.cnblogs.com/dolphin0520/p/3920373.html