1 从一个程序说起
public class Test {
//自增变量
volatile static int count=0;
//每个线程增加100次
public /*synchronized*/ static void addPlusPlus(){
for (int i = 0; i < 100; i++) {
count++;
}
}
public static void main(String[] args) {
Thread[] threads = new Thread[1000];
//创建1000个线程
for (int j = 0; j < 1000; j++) {
Thread thread = new Thread(Test::addPlusPlus);
threads[j]= thread;
}
//线程依次启动
for (int i = 0; i < 1000; i++) {
threads[i].start();
}
//当前面的线程都执行完了再输出结果
while (Thread.activeCount()>2){
Thread.yield();
}
System.out.println(count);
}
}
输出结果:
第一次 99951
第二次 99952
第三次 99883
从输出结果可知,volatile虽然能保证有序性,但是不能保证原子性。
2 加了volatile为什么不能保证原子性呢?
下面我们从字节码的角度分析一下:
自增方法编译后产生的字节码如上图所示
由于2 5 6 7字节码不具有原子性,执行过程中可能被其他线程强行插入。那么就出现这么一个情况。
count最开始在内存中为0,然后线程1和线程2把count=0读到自己的副本中,然后执行自增操作,然后线程1把count=1写入主存,由于线程2已经执行了getfield
,虽然变量是volitail修饰,但线程2已经用不到count变量,因此不会从主存里更新count,然后线程2写回主存count=1;此时虽然++两次但是结果却是1;
如有错误,欢迎指正。