最常需要解决的问题是i++ 这个语义在多线程中是不安全的。虽然从语法上看上去是一个操作,实际上分为了三步。取出i的值,i+1,将i+1的计算结果赋值给i。假设i的初始值为5,一种不安全的情况如下:
-123456线程1i(5)i+1(6)i(6)线程2i(5)i+1(6)i(6)也就是说两个线程并发执行i++操作,所得的结果都是6 !而我们期望应该是一个是6,一个是7。如果这个用于数据库主键的生成,这将导致严重的主键冲突。
value:用volatile修饰使得变量对所有线程可见,代表实际值,AtomicInteger.get()就是返回这个的值。
valueOffset:value的偏移地址
incrementAndGet():这是也就是我们常用的保证原子操作递增方法,它实际上调用了unsafe.getAndAddInt()
Unsafe类并不能直接查看到源码,只能通过反编译来看。
先看看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