AtomicInteger源码解析
原子类,以常用的AtomicInteger为例,分析其源码,了解它的实现。
AtomicInteger的源码不多,有两个比较重要类属性,unsafe和valueOffset。
unsafe
Unsafe,一个不安全的操作类(方法大部分为native),包括有控制内存的申请与释放(堆外)等,AtomicInteger用到的是Unsafe类中的CAS(compareAndSwapInt)操作。
由于这个类比较特别,所以Java对其作了限制,限制获取其实例,它的构造方法是私有的,只允许能通过getUnsafe
方法来获取对象实例,但是其方法会判断类加载器是否是启动类加载器,若不是则会抛出异常,代码如下:
1 2 3 4 5 6 7 8
| public static Unsafe getUnsafe() { Class var0 = Reflection.getCallerClass(); if (!VM.isSystemDomainLoader(var0.getClassLoader())) { throw new SecurityException("Unsafe"); } else { return theUnsafe; } }
|
所以获取Unsafe实例有两个方法:
- 使用启动类加载器加载主代码
- 使用反射
使用示例
使用lombok
插件依赖,通过Unsafe获取修改类对象属性,这里涉及到偏移量,下面的valueOffset会介绍偏移量的理解。
User.java
1 2 3 4 5 6
| @Data public class User { public int num; private Integer id; private String name; }
|
Main.java
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| public class Main { public static void main(String[] args) throws Exception{ Field theUnsafe = Unsafe.class.getDeclaredField("theUnsafe"); theUnsafe.setAccessible(true); Unsafe unsafe = (Unsafe) theUnsafe.get(Unsafe.class); User user = new User(); long id = unsafe.objectFieldOffset(User.class.getDeclaredField("id")); unsafe.putObject(user, id, 334); long name = unsafe.objectFieldOffset(User.class.getDeclaredField("name")); unsafe.putObject(user, name, "233"); System.out.println("user = " + user); System.out.println("unsafe.getObject(user, name) = " + unsafe.getObject(user, name)); long num = unsafe.objectFieldOffset(User.class.getDeclaredField("num")); unsafe.putInt(user, num, 111); System.out.println("user = " + user); } }
|
valueOffset
AtomicInteger有一个对象属性value,valueOffset为类属性,它表示类对象value属性偏移量,可以这样理解:AtomicInteger类对象的实例的每个固定偏移量的位置是value属性的存储位置。打个比方,想找到中指的位置,只要知道从边缘的手指处开始数,数到3的那个手指就是中指,只要是个正常的人类,按这个偏移量3去数都会找到中指的位置。
AtomicInteger通过Unsafe的方法去获取对象属性value的偏移量,代码如下:
1 2 3 4 5 6
| static { try { valueOffset = unsafe.objectFieldOffset (AtomicInteger.class.getDeclaredField("value")); } catch (Exception ex) { throw new Error(ex); } }
|
自旋CAS
自旋并进行CAS(compareAndSwapInt)是AtomicInteger的核心,以常用的incrementAndGet
来说明,源码如下:
1 2 3
| public final int incrementAndGet() { return unsafe.getAndAddInt(this, valueOffset, 1) + 1; }
|
incrementAndGet
方法或者说AtomicInteger类,其实大部分都是基于unsafe的CAS包装和加料,unsafe的getAndAddInt
方法源码如下:
1 2 3 4 5 6 7
| 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; }
|
这里可以看到自旋的进行compareAndSwapInt
直至成功为止,如果其他线程成功更新了value的值,那么通过unsafe的getIntVolatile
方法可以获取到最新值再进行更新。