理解对多线程并发资源的保护——从AtomicInteger源代码出发

发布时间:2019-08-06 发布网站:脚本宝典
脚本宝典收集整理的这篇文章主要介绍了理解对多线程并发资源的保护——从AtomicInteger源代码出发脚本宝典觉得挺不错的,现在分享给大家,也给大家做个参考。

前言

Java线程中的java.util.concurrent.atomic包里面都是类都是针对多线程下的原子变量,有包括AtomicInteger, AtomicBoolean等等多种变量的原子化实现。

本次我们将会解读AtomicInteger的码,对变量的原子化思路进行一个理解。这也会对理解现实场景中,多线程程序原子化使用某个资源也有更好的理解。我们抽取几个主要的方法进行解读。

画个UML

AtomicInteger主要实现了Number接口,这个接口提供的方法都是将原子变量值转换为其他类型值的接口。

而主要的原子化特性则是通过持有jdk.internal.misc.Unsafe对象实现Proxy模式进行实现。

理解对多线程并发资源的保护——从AtomicInteger源代码出发

初始化

两个静态变量十分清晰:

U

是调用了jdk.internal.misc.Unsafe对象,协助后面的原子性更新特性。

VALUE

则是使用了objectFieldOffset获取了对象在JVM内存中的地址。该方法的具体实现则是一个native方法,通过C或C++对JVM内部进行操作。暂时不作细究。
    PRivate static final jdk.internal.misc.Unsafe U = jdk.internal.misc.Unsafe.getUnsafe();
    private static final long VALUE = U.objectFieldOffset(AtomicInteger.class, "value");
    private volatile int value;

构造函数
AtomicInteger(int)

将传入int值,写入为成员变量,令对象持有该值。

主要API源码解读

int get()

比较直观的实现,直接返回被声明为volatile的value。

void set(int)

比较直观的实现,将输入值赋值到为volatile的value。

int getAndIncrement()

有效的逻辑,在调用Unsafe对象的下面两个方法:
public final int getAndAddInt(Object o, long offset, int delta) {
        int v;
        do {// 循环
            // getIntVolatile是一个native方法:
            // 以内存地址,及Object对象确定获取内存中的volatile值
            v = getIntVolatile(o, offset);
        } while (!weakCompareAndSetInt(o, offset, v, v + delta));
        //调用下方的weakCompareAndSetInt方法,直到更新成功则退出循环
        return v;
    }

public final boolean weakCompareAndSetInt(Object o, long offset,
                                              int expected,
                                              int x) {
        // 调用native方法compareAndSetInt
        // 以对象类型o,内存地址offset,期望值exPEcted,输入值x
        // 按照对象类型及内存地址获得期待值,对比后更新为输入值。
        return compareAndSetInt(o, offset, expected, x);
    }

int getAndDecrement()

类似于getAndIncreement,调用Unsafe,对内存中数值进行操作。

小结及延伸思考

通过回顾针对AtomiInteger的几个基本方法原子化操作的解读,我们可以理解为原子操作的几个要点:

  1. 采用volatile类型的特性,直接读取内存中的值,忽略掉VM中的值。
  2. 采用对比的方法,对比对象类型,内存地址,原有值等维度,核对后进行赋值。(具体要检查native方法的代码)
  3. Atomic类忽略了值的次序性,尽可能以值的唯一性保证其原子性。

对于数据库记录的原子性的解决方案,也有类似的解决方案,多线程占用某个数据库资源更新时,我们也可以先做读取,核对值或版本号后再作更新。

对于数据库相关的原子性保证更新,我们下一篇再聊聊。

脚本宝典总结

以上是脚本宝典为你收集整理的理解对多线程并发资源的保护——从AtomicInteger源代码出发全部内容,希望文章能够帮你解决理解对多线程并发资源的保护——从AtomicInteger源代码出发所遇到的问题。

如果觉得脚本宝典网站内容还不错,欢迎将脚本宝典推荐好友。

本图文内容来源于网友网络收集整理提供,作为学习参考使用,版权属于原作者。
如您有任何意见或建议可联系处理。小编QQ:384754419,请注明来意。