AtomicInteger源码分析

来源:互联网 发布:类似炫浪网络社区 编辑:程序博客网 时间:2024/05/17 02:41

要解决什么问题

最常需要解决的问题是i++ 这个语义在多线程中是不安全的。虽然从语法上看上去是一个操作,实际上分为了三步。取出i的值,i+1,将i+1的计算结果赋值给i。假设i的初始值为5,一种不安全的情况如下:

- 1 2 3 4 5 6 线程1 i(5) i+1(6) i(6) 线程2 i(5) i+1(6) i(6)

也就是说两个线程并发执行i++操作,所得的结果都是6 !而我们期望应该是一个是6,一个是7。如果这个用于数据库主键的生成,这将导致严重的主键冲突。

如何解决

AtomicInteger类

// setup to use Unsafe.compareAndSwapInt for updatesprivate 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;public final int incrementAndGet() {        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;    }

value:用volatile修饰使得变量对所有线程可见,代表实际值,AtomicInteger.get()就是返回这个的值。

valueOffset:value的偏移地址

incrementAndGet():这是也就是我们常用的保证原子操作递增方法,它实际上调用了unsafe.getAndAddInt()

Unsafe类并不能直接查看到源码,只能通过反编译来看。

Unsafe类

public final native boolean compareAndSwapInt(Object object, long valueOffset, int expected, int update);public final int getAndAddInt(Object object, long valueOffset, int increament) {    int expected;    do {        expected = this.getIntVolatile(object, valueOffset);    } while(!this.compareAndSwapInt(object, valueOffset, expected, expected + increament));    return expected;}

先看看compareAndSwapInt方法,方法用native修饰,表示是一个本地方法,调用的是CAS指令执行,这是整个过程是一个原子操作。功能是如果地址(由object和valueOffset共同确定)中的值与expected相同,则设置改地址的值为update,并返回true,否则不更新且返回false。

再看看getAndAddInt(),在这个方法中调用了this.getIntVolatile(object, valueOffset),表示获取地址中的值,那么整个getAndAddInt()方法的语义是:

1.获取到对象中存储value地址的值

2.然后再回去比较是否相等,相等则更新,否则从1开始重试直到成功。

为什么AtomicInteger操作并不会出现线程问题:当两个线程同时进入do-while循环中后,只有第一个线程能执行更新操作成功并退出循环,第二个线程试图执行赋值操作因为地址中的值不一致而不会更新,只能重新执行循环,重新执行循环获得到的是最新的value值,所以此处并不会出现线程问题。

该类的中其它方法与本文类似,故不重复分析。

注:本文源码为JDK1.8

0 0
原创粉丝点击