AtomicInteger / AtomicLong源码解析
AtomicInteger / AtomicLong
AtomicInteger和AtomicLong是保证线程安全的整数型类,两者的实现思想大体相同,所以这里只以AtomicLong
举例
例
public static void AtomicNumber() throws InterruptedException {
AtomicLong val = new AtomicLong(0);
Thread a = new Thread(() -> {
for (int i = 0; i < 100; i++)
val.addAndGet(1);
}, "a-thread");
Thread b = new Thread(() -> {
for (int i = 0; i < 100; i++)
val.addAndGet(1);
}, "b-thread");
a.start();
b.start();
a.join();// 主线程等待a线程执行完毕继续执行
b.join();// 主线程等待b线程执行完毕继续执行
System.out.println(val);
}
上述代码中,由2个线程执行for循环对val变量进行累加,输出结果始终是200,已经达到了线程安全的要求,具体如何实现的呢?看看 AtomicLong
的源码吧!
ActiomicLong 类加载
private static final long valueOffset;//内存地址偏移量,更方便于找到value值
static {
try {//获取内存地址偏移量【不用深究,知道就行了】
valueOffset = unsafe.objectFieldOffset(AtomicLong.class.getDeclaredField("value"));
} catch (Exception ex) { throw new Error(ex); }
}
AtomicLong.class.getDeclaredField 方法
/**
* 返回一个字段对象,该对象反映由此Class对象表示的类或接口
* 的指定声明字段。
* Name参数是一个字符串,它指定所需字段的名称。如果此Class
* 对象表示数组类型,则此方法找不到该数组类型的长度字段。
* @param name 执行静态代码块时传入的"value"
*/
public Field getDeclaredField(String name)
throws NoSuchFieldException, SecurityException {
checkMemberAccess(Member.DECLARED, Reflection.getCallerClass(), true);//验证
Field field = searchFields(privateGetDeclaredFields(false), name);
if (field == null) {
throw new NoSuchFieldException(name);
}
return field;
}
unsafe.objectFieldOffset 方法:获取给定对象字段的内存偏移量
这个偏移量是相对于对象头(header)的,可以用来直接通过内存地址访问对象的字段,而不需要通过Java的字段访问机制
public native long objectFieldOffset(Field var1);
AtomicLong对象初始化
private volatile long value;//存储数据,源码中通过内存偏移量来获得其值
public AtomicLong(long initialValue) {
value = initialValue;//赋值
}
加 操作
addAndGet 方法
private static final Unsafe unsafe = Unsafe.getUnsafe();
private static final long valueOffset;
public final long addAndGet(long delta) {
/**
* 调用静态属性unsafe的getAndAddLong方法
* 参数
* this AtomicLong类对象
* valueOffset 内存地址偏移量
* delta 加数
*/
return unsafe.getAndAddLong(this, valueOffset, delta) + delta;
}
Unsafe.getAndAddLong 方法
/**
* 修改值
* @param var1 obj:要操作的对象
* @param var2 offset:偏移量
* @param var4 增量
*/
public final long getAndAddLong(Object var1, long var2, long var4) {
long var6;
// 以自旋的形式,不断访问对象,只要对象中值与期待值相同,则修改并结束循环
do {
var6 = this.getLongVolatile(var1, var2);
/*在while循环判断中,一直在调用 compareAndSwapLong 方法*/
} while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));
return var6;
}
/**
* 读取对象 o 中偏移量 offset 处的 long 类型字段的值
* @param var1 obj:要操作的对象
* @param var2 offset:偏移量
*/
public native long getLongVolatile(Object var1, long var2);
/**
* 以原子方式比较并交换某个对象的一个 long 类型的字段:首先
* 比较 var1 中偏移量为 var2 的字段的当前值与 var4 是否相等。
* 若相等,说明没有其他线程修改过这个字段的值,那么方法会将该
* 字段更新为 var6 值,并返回 true。如果不相等,说明其他线程
* 可能已经修改过这个字段的值,则不做任何操作,并返回 false
*
* @param var1 obj:要操作的对象。
* @param var2 offset,在 obj 中要操作的字段的内存偏移量。
* @param var4 expected,期望的当前值。
* @param var6 updated,要更新的新值,上一步中传入的是(var6+var4)。
*/
public final native boolean compareAndSwapLong(Object var1, long var2, long var4, long var6);
总结
看得出,AtomicInteger
实现线程安全的方式是CAS机制
(compare and swap),也可以称之是乐观锁的一种体现,首先比对内存中的值和刚获取的值是否一样,一样则认为没有其他线程在使用此对象,随即进行更改,不一样则证明其他线程正在使用,那么当前线程就不改它并不断通过自旋的方式判断此对象的值。
线程自旋也有坏处:自旋线程并不处于阻塞,等待或超时等待状态,而是处于执行状态,它在不停的访问对象内存的数据,然后比对,这将造成一定程度上的性能浪费。
本文来自博客园,作者:勤匠,转载请注明原文链接:https://www.cnblogs.com/JarryShu/articles/18184992
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现