不使用volatile,开100个进程自增:
package com.wanggang.java.test;
public class ThreadTest3 {
public static int countInt;
public volatile static int countIntVolatile;//volatile告诉编译器该值是随时发生变化的,不进行编译优化
public static void main(String[] args) {
for(int i=0;i<10;i++){
new Thread(new Runnable() {//new 100个线程处理自增
public void run() {//重写runnable里面的run()方法
countThread();
System.out.println(countInt);
//System.out.println(countIntVolatile);
}
}).start();
}
System.out.println("The Result of countInt is "+countInt);
//System.out.println("The Result of countIntVolatile is"+countIntVolatile);
}
public static void countThread(){
try {
Thread.sleep(10);//让线程睡10ms让结果明显
} catch (InterruptedException e) {
e.printStackTrace();
}
countInt++;
countIntVolatile++;
}
}
结果是:
The Result of countInt is 0
2
3
2
2
4
9
8
7
6
5
可以看出在有三个进程读到了countInt=1时,写countInt=2写了三次,最后也没有加到10。
加上volatile关键字后:(去掉上代码注释)
结果为:
The Result of countIntVolatile is 0
2
2
3
4
5
6
10
9
8
8
从两个2,两个8也能得出volatile并没有实现一个线程对countIntVolatile 的 (读写)是一个原子操作。
为什么?
jvm主内存与线程内存交互:
一个线程对一个变量进行操作时,先对通过找到这个变量的引用来read它在堆内存中对应的值,然后load到线程本地内存,之后的use和asign(赋值)都在线程本地内存中进行。最后store到线程本地内存,write到主内存,实现线程内存与主内存之间的同步。需要注意到,这一个完整的(读写)并不是一个原子操作。
所以,当线程A读到countInt=1时,还没对主内存进行write时,线程B已经read,load到countInt=1,所以当线程B也write到主内存时会出现多个输出为2。
而volatile关键字只能保证在主内存中读取的是变量的最新值,所以加了volatile后也一样的。
所以volatile不是线程安全的,同步锁不能省。