先从一段代码开始
private static AtomicInteger a = new AtomicInteger(0);
private static int anInt = 0;
public static void main(String[] args) throws InterruptedException {
Thread[] threads = new Thread[100];
for (int i = 0; i < threads.length ; i++) {
threads[i] = new Thread(()->{
for (int j = 0; j < 1000; j++) {
a.incrementAndGet();
anInt++;
}
});
threads[i].start();
}
Thread.sleep(100);
System.out.println("原子类:"+a.get());
System.out.println("基本数据类型int:"+anInt);
}
其结果如下
原子类:100000
基本数据类型int:63516
原子了每次运行的结果都能达到预期,而int基本无法达到。
查看incrementAndGet()方法源码
public final int incrementAndGet() {
return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
}
定位到Unsafe.class
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);
在回写到内存时,比较期望值,若发生改变则重新读取变量再执行一次操作。这即是比较并交换的思想:CAS (compareAndSwap)
那么在这个过程中必然涉及的两个问题:
1、期望值会不会是多个线程多次回写的结果,导致期望值相等?
2、在判断期望值相等的一瞬间,会不会有线程将变量的值修改?
对于问题一,加上版本号即可解决。至于问题二,则需要查看C++代码,最终在C++代码中找到实现
lock cmpxchg 指令
lock指令在执行后面指令的时候锁定一个北桥信号,任何线程都得等我执行完才能执行,这样就保证了CAS的原子性
网友评论