浅谈AtomicInteger

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实例有两个方法:

  1. 使用启动类加载器加载主代码
  2. 使用反射

使用示例

使用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方法可以获取到最新值再进行更新。


浅谈AtomicInteger
https://blog.gugu.dev/2024-02-21/浅谈AtomicInteger/
作者
MinMin
发布于
2024年2月21日
许可协议