volatile 修饰的int与AtomicInteger原子类型的使用区别
public class Demo01 {
public static volatile int num;
//public static AtomicInteger num2 = new AtomicInteger();
public static void main(String[] args) {
ExecutorService threadPool = Executors.newFixedThreadPool(5);
for (int i = 0; i < 5; i++) {
threadPool.execute(() -> {
for (int i1 = 0; i1 < 1000; i1++) {
add();
}
});
}
System.out.println(num);
}
public static void add() {
num++;
}
}
此段代码num的执行结果偶尔会小于4000。是因为volatile只保证可见性即每个线程去获取num时总是直接去主内存中不会去工作内存中,可以有效避免了脏读。但是自增操作是非原子性的。
自增操作的过程分为三步:
- 从内存中获取
- 对值进行修改
- 写回内存
例如:num=10 当Thread1从主内存中获取到值后被阻塞了。Thread2获取值后进行自增操作,并写回主内存。此时num=11。因为自增操作不是原子性的,故Thread1获取时间片后继续执行并返回,此时返回的值会覆盖之前的。所以造成现在的结果。
如果想使用原子性的自增操作使用AtomicInteger 即可,AtomicInteger是利用CAS保证原子操作的。
本文参考:https://www.cnblogs.com/dolphin0520/p/3920373.html