资讯专栏INFORMATION COLUMN

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

longmon / 2848人阅读

摘要:前言线程中的包里面都是类都是针对多线程下的原子变量,有包括等等多种变量的原子化实现。这也会对理解现实场景中,多线程程序原子化使用某个资源也有更好的理解。

前言

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

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

画个UML

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

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

初始化

两个静态变量十分清晰:

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的几个基本方法原子化操作的解读,我们可以理解为原子操作的几个要点:

采用volatile类型的特性,直接读取内存中的值,忽略掉VM中的值。

采用对比的方法,对比对象类型,内存地址,原有值等维度,核对后进行赋值。(具体要检查native方法的代码)

Atomic类忽略了值的次序性,尽可能以值的唯一性保证其原子性。

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

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

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/76804.html

相关文章

  • Java 并发编程系列之带你了解多线程

    摘要:的内置锁是一种互斥锁,意味着最多只有一个线程能持有这种锁。使用方式如下使用显示锁之前,解决多线程共享对象访问的机制只有和。后面会陆续的补充并发编程系列的文章。 早期的计算机不包含操作系统,它们从头到尾执行一个程序,这个程序可以访问计算机中的所有资源。在这种情况下,每次都只能运行一个程序,对于昂贵的计算机资源来说是一种严重的浪费。 操作系统出现后,计算机可以运行多个程序,不同的程序在单独...

    Elle 评论0 收藏0
  • 线程中那些看不见陷阱

    摘要:多线程编程就像一个沼泽,中间遍布各种各样的陷阱。但是在多线程编程或者说是并发编程中,有非常多的陷阱被埋在底层细节当中。线程池类中用于控制线程池状态和线程数的控制变量就是一个类型的字段。 多线程编程就像一个沼泽,中间遍布各种各样的陷阱。大多数开发者绝大部分时间都是在做上层应用的开发,并不需要过多地涉入底层细节。但是在多线程编程或者说是并发编程中,有非常多的陷阱被埋在底层细节当中。如果不知...

    phodal 评论0 收藏0
  • 猫头鹰深夜翻译:核心JAVA并发(一)

    摘要:简介从创建以来,就支持核心的并发概念如线程和锁。这篇文章会帮助从事多线程编程的开发人员理解核心的并发概念以及如何使用它们。请求操作系统互斥,并让操作系统调度程序处理线程停放和唤醒。 简介 从创建以来,JAVA就支持核心的并发概念如线程和锁。这篇文章会帮助从事多线程编程的JAVA开发人员理解核心的并发概念以及如何使用它们。 (博主将在其中加上自己的理解以及自己想出的例子作为补充) 概念 ...

    Richard_Gao 评论0 收藏0
  • 深入理解 Java 多线程系列(1)——一个简单需求并行改造 & Java多线程通信问题

    摘要:所以接下来,我们需要简单的介绍下多线程中的并发通信模型。比如中,以及各种锁机制,均为了解决线程间公共状态的串行访问问题。 并发的学习门槛较高,相较单纯的罗列并发编程 API 的枯燥被动学习方式,本系列文章试图用一个简单的栗子,一步步结合并发编程的相关知识分析旧有实现的不足,再实现逻辑进行分析改进,试图展示例子背后的并发工具与实现原理。 本文是本系列的第一篇文章,提出了一个简单的业务场景...

    ruicbAndroid 评论0 收藏0
  • 0到1实现自己阻塞队列(下)

    摘要:在上一篇文章从到实现自己的阻塞队列上中,我们已经实现了一个可以使用的阻塞队列版本。插入锁队列未满的条件变量弹出锁队列非空的条件变量最后我们要对和方法中的调用做出一些调整。 在上一篇文章《从0到1实现自己的阻塞队列(上)》中,我们已经实现了一个可以使用的阻塞队列版本。在这篇文章中,我们可以继续我们的冒险之旅,将我们的阻塞队列提升到接近JDK版本的水平上。 更进一步优化效率 我们一直使用的...

    XFLY 评论0 收藏0

发表评论

0条评论

最新活动
阅读需要支付1元查看
<