先看一段代码
public class VolatileNoAtomic extends Thread{
private static int count=0;
private static void addCount(){
for (int i = 0; i < 1000; i++) {
count++ ;
}
System.out.println(count);
}
public static void main(String[] args) {
for (int i = 0; i < 10; i++) {
new Thread(new Runnable() {
@Override
public void run() {
VolatileNoAtomic.addCount();
}
}).start();
}
}
}
输出结果
1000
2023
2127
3127
4127
5127
6127
7127
8127
9127
每次运行的结果都不一样
原因:在当前的Java内存模型下,线程可以把变量保存在本地内存(比如机器的寄存器)中,而不是直接在主存中进行读写。这就可能造成一个线程在主存中修改了一个变量的值,而另外一个线程还继续使用它在寄存器中的变量值的拷贝,造成数据的不一致。
而有一个很好的解决方法,就是加上Volatile关键字
private static volatile int count=0;
但是也会有瑕疵
2000
3000
2000
5000
4000
6000
7000
8000
9162
9662
原因是指定了Volatile,这就指示JVM,这个变量是不稳定的,每次使用它都到主存中进行读取,但是,Volatile修饰的变量不具有原子性!每一个线程都有一个线程栈,如图:
而JVM虚拟机只是保证从主内存加载到线程工作内存的值是最新的,如果线程1,线程2同时load到count=5,线程1进行+1操作,那么count就变成6,并写入到主内存中,由于线程2已经完成了了load,他得到的count=5,而不是6,所以最终的结果是6。
解决方法
将count声明为AtomicInteger,
private static AtomicInteger count=new AtomicInteger(0);
private static void addCount(){
for (int i = 0; i < 1000; i++) {
count.incrementAndGet();
}
那么这样做就能让count的每一次操作都是原子的。
结果:
3315
3877
4000
3315
5000
6544
7000
8000
9000
10000
每次得到的结果都是10000
**
总结:Volatile能使变量在进程中可见但是不具备原子性。
**