volatile并不能保证在运算时多线程安全,例如下面的例子
public class VolatileTest {
public static volatile int race = 0;
public static void increase(){
race++;
}
private static final int THREADS_COUNT = 20;
public static void main(String[] args){
Thread[] threads = new Thread[ THREADS_COUNT ];
for(int i=0; i<THREADS_COUNT; i++){
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
for(int j=0; j<10000; j++)
increase();
}
});
threads[i].start();
}
while(Thread.activeCount() > 1){
Thread.yield();
}
System.out.println(race);
}
}
期待的结果是2000,结果却不是,小于2000.
在increase方法假设synchronized关键字之后就变成了2000。
下面来看下increase方法的字节码 (这个字节码只是说明 ++操作的步骤,和volatile关键字无关)
public static void increase();
Code:
0: getstatic #14 // Field race:I
3: iconst_1
4: iadd
5: putstatic #14 // Field race:I
8: return
getstatic 获取指定类的静态域,并将其值压入栈顶
iconst_1 int型常量值1进栈
iadd 栈顶两int型数值相加,并且结果进栈
putstatic 为指定的类的静态域赋值
由于中间需要多个步骤,任意时刻都可能有其它线程切入。
getstatic 时,volatile保证了race的值在此时是正确的,但是在执行 iconst-1 和 iadd 时,其它线程可能已经把race的值加大了。而在操作栈顶的值就变成了过期的数据。