volatile可见性和禁止指令重排序这里就不再赘述,主要是讨论一下为什么volatile修饰的变量不能够保证原子性,最常见的就是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 i = 0; i < 10000; i++) {
increase();
}
}
});
threads[i].start();
}
// 等待所有累加线程都结束
while (Thread.activeCount() > 1)
Thread.yield();
System.out.println(race);
}
}
如上所示,自增结束后race的值并不是200000,主要是因为race的自增操作是非原子的。查看反汇编生成的字节码文件,如下所示
public static void increase();
Code:
0: getstatic #2 // Field race:I
3: iconst_1
4: iadd
5: putstatic #2 // Field race:I
8: return
我们可以看到,对于increase函数,一共会生成5条字节码指令,而对于其中的race++操作,对应于前4条指令,即得到race的值,将race值+1,再将race的值同步会内存中。volatile可以保证可见性,即我们获得的race值是主内存中最新的,但是当我们执行自增操作的时候,并不能保证现在没有其他线程对volatile的变量执行自增操作,其他线程可能已经将race的值变大了,此时操作站顶的值就成了过期的数据,所以最后的结果是不准确的。