首先我们要知道i++在多线程环境下会出现运算错误的情况
public class Test08 {
//static AtomicInteger i = new AtomicInteger(0);
static int i = 0;
public static void main(String[] args) throws InterruptedException {
test();
Thread.sleep(3000);
System.out.println("最终的值"+i);
}
public static void test(){
new Thread(new Runnable() {
public void run() {
for (int a=0; a<4000; a++){
//System.out.println("当前线程"+Thread.currentThread().getName()+i.getAndIncrement());
System.out.println("当前线程"+Thread.currentThread().getName()+(i++));
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for (int a=0; a<4000; a++){
//System.out.println("当前线程"+Thread.currentThread().getName()+i.getAndIncrement());
System.out.println("当前线程"+Thread.currentThread().getName()+(i++));
}
}
}).start();
}
}
多运行几次就会有输出结果不对的情况
....
最终的值7999
从结果可以看出int的i++是无法保证原子性的, 下面把int改成 java.util.concurrent.atomic包下的AtomicInteger
public class Test08 {
static AtomicInteger i = new AtomicInteger(0);
//static int i = 0;
public static void main(String[] args) throws InterruptedException {
test();
Thread.sleep(3000);
System.out.println("最终的值"+i);
}
public static void test(){
new Thread(new Runnable() {
public void run() {
for (int a=0; a<4000; a++){
System.out.println("当前线程"+Thread.currentThread().getName()+i.getAndIncrement());
//System.out.println("当前线程"+Thread.currentThread().getName()+(i++));
}
}
}).start();
new Thread(new Runnable() {
public void run() {
for (int a=0; a<4000; a++){
System.out.println("当前线程"+Thread.currentThread().getName()+i.getAndIncrement());
//System.out.println("当前线程"+Thread.currentThread().getName()+(i++));
}
}
}).start();
}
}
结果:
最终的值8000
可以看出AtomicInteger的getAndIncrement()方法在多线程环境下是可以保证原子性的
原理:
AtomicInteger是对int类型的一个封装,提供原子性的访问和更新操作,其原子性操作的实现是基于CAS(compare-and-swap)技术。
所谓CAS,表现为一组指令,当利用CAS执行试图进行一些更新操作时。会首先比较当前数值,如果数值未变,代表没有其它线程进行并发修改,则成功更新。如果数值改变,则可能出现不同的选择,要么进行重试,要么就返回是否成功。也就是所谓的“乐观锁”。
从AtomicInteger的内部属性可以看出,它依赖于Unsafe提供的一些底层能力,进行底层操作;以volatile的value字段,记录数值,以保证可见性。
// setup to use Unsafe.compareAndSwapInt for updates
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
static {
try {
valueOffset = unsafe.objectFieldOffset
(AtomicInteger.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
private volatile int value;
valueOffset 就是value在内存中的偏移量
再看getAndIncrement()方法
public final int getAndIncrement() {
return unsafe.getAndAddInt(this, valueOffset, 1);
}
调用了Unsafe类的方法
public final int getAndAddInt(Object var1, long var2, int var4) {
int var5;
do {
var5 = this.getIntVolatile(var1, var2);
} while(!this.compareAndSwapInt(var1, var2, var5, var5 + var4));
return var5;
}
通过循环调用Unsafe的compareAndSwapInt方法比较并修改, 多线程环境下成功退出循环 , 失败继续循环获取内存中的值
/*
* 比较并修改值
* 1、需要修改的对象
* 2、要更改的属性的内存偏移量
* 3、预期的值
* 4、设置的新值
* */
public final native boolean compareAndSwapInt(Object var1, long var2, int var4, int var5);