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),也可以称之是乐观锁的一种体现,首先比对内存中的值和刚获取的值是否一样,一样则认为没有其他线程在使用此对象,随即进行更改,不一样则证明其他线程正在使用,那么当前线程就不改它并不断通过自旋的方式判断此对象的值。

线程自旋也有坏处:自旋线程并不处于阻塞,等待或超时等待状态,而是处于执行状态,它在不停的访问对象内存的数据,然后比对,这将造成一定程度上的性能浪费。

posted @   勤匠  阅读(19)  评论(0编辑  收藏  举报
编辑推荐:
· AI与.NET技术实操系列:基于图像分类模型对图像进行分类
· go语言实现终端里的倒计时
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
阅读排行:
· 分享一个免费、快速、无限量使用的满血 DeepSeek R1 模型,支持深度思考和联网搜索!
· 基于 Docker 搭建 FRP 内网穿透开源项目(很简单哒)
· ollama系列01:轻松3步本地部署deepseek,普通电脑可用
· 25岁的心里话
· 按钮权限的设计及实现
点击右上角即可分享
微信分享提示