首页 > 其他分享 >Atomic原子类

Atomic原子类

时间:2024-04-06 10:30:43浏览次数:26  
标签:final int 更新 原子 volatile Atomic public

Atomic类位于java.util.concurrent.atomic包下,它们利用CAS(Compare-And-Swap)操作来保证线程安全性,而无需使用传统的锁机制。这些类提供了一种轻量级的同步机制,适用于多线程环境下对共享变量的高效更新。JDK中提供了12个原子操作类如图所示:
在这里插入图片描述

原子更新基本类型

使用原子的方式更新基本类型,Atomic包提供了以下3个类。

  • AtomicBoolean: 原子更新布尔类型。
  • AtomicInteger: 原子更新整型。
  • AtomicLong: 原子更新长整型。

以下是AtomicInteger的源码:

public class AtomicInteger extends Number implements java.io.Serializable {
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    private static final long valueOffset;
    static {
        try {
            //用于获取value字段相对当前对象的“起始地址”的偏移量
            valueOffset = unsafe.objectFieldOffset(AtomicInteger.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }

    private volatile int value;

    //返回当前值
    public final int get() {
        return value;
    }

    //递增加detla
    public final int getAndAdd(int delta) {
        //三个参数,1、当前的实例 2、value实例变量的偏移量 3、当前value要加上的数(value+delta)。
        return unsafe.getAndAddInt(this, valueOffset, delta);
    }

    //递增加1
    public final int incrementAndGet() {
        return unsafe.getAndAddInt(this, valueOffset, 1) + 1;
    }
...
}

原子更新数组

通过原子的方式更新数组里的某个元素,Atomic包提供了以下的3个类:

  • AtomicIntegerArray: 原子更新整型数组里的元素。
  • AtomicLongArray: 原子更新长整型数组里的元素。
  • AtomicReferenceArray: 原子更新引用类型数组里的元素。

这三个类的最常用的方法是如下两个方法:get(int index):获取索引为index的元素值。compareAndSet(int i,E expect,E update): 如果当前值等于预期值,则以原子方式将数组位置i的元素设置为update值。

public class Demo5 {
    public static void main(String[] args) throws InterruptedException {
        AtomicIntegerArray array = new AtomicIntegerArray(new int[] { 0, 0 });
        System.out.println(array);//[0, 0]
        System.out.println(array.getAndAdd(1, 2));//0
        System.out.println(array);//[0, 2]
    }
}

原子更新引用类型

Atomic包提供了以下三个类:

  • AtomicReference: 原子更新引用类型。
  • AtomicStampedReference: 原子更新引用类型, 内部使用Pair来存储元素值及其版本号。
  • AtomicMarkableReferce: 原子更新带有标记位的引用类型。

这三个类提供的方法都是首先构造一个引用对象,然后把引用对象set进Atomic类,然后调用compareAndSet等一些方法去进行原子操作,原理都是基于Unsafe实现,但AtomicReferenceFieldUpdater略有不同,更新的字段必须用volatile修饰。
用代码简单使用下AtomicReference类:

public class AtomicReferenceTest {
    
    public static void main(String[] args){

        // 创建两个Person对象,它们的id分别是101和102。
        Person p1 = new Person(101);
        Person p2 = new Person(102);
        // 新建AtomicReference对象,初始化它的值为p1对象
        AtomicReference ar = new AtomicReference(p1);
        // 通过CAS设置ar。如果ar的值为p1的话,则将其设置为p2。
        ar.compareAndSet(p1, p2);

        Person p3 = (Person)ar.get();
        System.out.println("p3 is "+p3);//102
        System.out.println("p3.equals(p1)="+p3.equals(p1));
    }
}

class Person {
    volatile long id;
    public Person(long id) {
        this.id = id;
    }
    public String toString() {
        return "id:"+id;
    }
}

原子更新字段类

Atomic包提供了四个类进行原子字段更新:

  • AtomicIntegerFieldUpdater: 原子更新整型的字段的更新器。
  • AtomicLongFieldUpdater: 原子更新长整型字段的更新器。
  • AtomicReferenceFieldUpdater: 原子更新引用类型的更新器。

这里的类都是基于反射的原子更新字段的值。要想原子地更新字段类需要两步:

  1. 因为原子更新字段类都是抽象类,每次使用的时候必须使用静态方法newUpdater()创建一个更新器,并且需要设置想要更新的类和属性。
  2. 更新类的字段必须使用public volatile修饰。

以下是类使用示例:

public class TestAtomicIntegerFieldUpdater {

    public static void main(String[] args){
        TestAtomicIntegerFieldUpdater tIA = new TestAtomicIntegerFieldUpdater();
        tIA.doIt();
    }

    public AtomicIntegerFieldUpdater<DataDemo> updater(String name){
        return AtomicIntegerFieldUpdater.newUpdater(DataDemo.class,name);

    }

    public void doIt(){
        DataDemo data = new DataDemo();
        System.out.println("publicVar = "+updater("publicVar").getAndAdd(data, 2));
 
}

class DataDemo{
    public volatile int publicVar=3;
    protected volatile int protectedVar=4;
    private volatile  int privateVar=5;

    public volatile static int staticVar = 10;
    //public  final int finalVar = 11;

    public volatile Integer integerVar = 19;
    public volatile Long longVar = 18L;

}

对于AtomicIntegerFieldUpdater 的使用稍微有一些限制和约束,约束如下:

  • 字段必须是volatile类型的,在线程之间共享变量时保证立即可见。
  • 字段的描述类型(修饰符public/protected/default/private)是与调用者与操作对象字段的关系一致。也就是说调用者能够直接操作对象字段,那么就可以反射进行原子操作。但是对于父类的字段,子类是不能直接操作的,尽管子类可以访问父类的字段。
  • 只能是实例变量,不能是类变量,也就是说不能加static关键字。
  • 只能是可修改变量,不能使final变量,因为final的语义就是不可修改。实际上final的语义和volatile是有冲突的,这两个关键字不能同时存在。
  • 对于AtomicIntegerFieldUpdater和AtomicLongFieldUpdater只能修改int/long类型的字段,不能修改其包装类型(Integer/Long)。

标签:final,int,更新,原子,volatile,Atomic,public
From: https://blog.csdn.net/weixin_57057153/article/details/137420476

相关文章

  • volatile 变量和 atomic 变量有什么不同
    该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点面试官:volatile变量和atomic变量有什么不同Volatile变量volatile关键字主要用于确保变量的可见性。当一个变量被声明为volatile时,它会保证所有线程都能看......
  • JUC:java内存模型(如何保证?可见性、原子性、有序性)
    文章目录java内存模型可见性解决方法原子性有序性流水线技术模式之Balking(犹豫)java内存模型JMM即JavaMemoryModel,它定义了主存、工作内存抽象概念,底层对应着CPU寄存器、缓存、硬件内存、CPU指令优化等。JMM体现在以下几个方面:原子性-保证指令不......
  • C++原子操作与内存序 1
    问题#include<iostream>#include<thread>intmain(){ intsum=0; autof=[&sum](){ for(inti=0;i<10000;i++) sum+=1; }; std::threadt1(f); std::threadt2(f); t1.join(); t2.join(); std::cout<<"thesum......
  • C++ 中的 volatile 和 atomic
    C++中的volatile和atomic0.TL;DRstd::atomic用于多线程并发场景,有两个典型使用场景:原子操作:对atomic变量的操作(读/写/自增/自减)仿佛受互斥量保护。一般通过特殊的机器指令实现,比使用互斥量更高效限制编译器/硬件对赋值操作的重新排序volatile和多线程并发没有......
  • 铭凡(MINISFORUM)全新高端子品牌“原子侠”新品亮相!
    铭凡(MINISFORUM)在九江召开高端子品牌发布会,正式宣布推出其全新高端科技子品牌“原子侠(AtomMan)”,并在发布会上推出多款此品牌下的全新产品,致力于为科技爱好者和追求极致性能的用户提供高品质的硬件和配件。在今天的品牌发布会上,原子侠同时推出了多款全新迷你主机产品,打开迷你......
  • atomic.SwapInt64
    atomic.SwapInt64汇编不再浪费时间去找源码了,因为atomic这部分的实现全部都依赖于底层的汇编指令​​会发现一件事情,那就是这里的实现和Store是几乎一致的,为什么呢?原因出在XCHGXCHG​XCHG​是x86架构汇编语言中的一条指令,它的全称是“ExchangeRegister/Memorywi......
  • atomic.StoreInt64
    atomic.StoreInt64源码依然将细节隐藏了起来,沟槽的TEXT sync∕atomic·StoreInt64(SB),NOSPLIT|NOFRAME,$0-16 GO_ARGS MOVQ $__tsan_go_atomic64_store(SB),AX CALL racecallatomic<>(SB) RET汇编​​解释把2​移入CX调用XCHGQ​,来原子性的修改CX和......
  • atomic.LoadInt64
    atomic.LoadInt64源码在经历了之前查看AddInt64​的经历后,我们可以确定LoadInt64​的代码位置TEXTruntime∕internal∕atomic·Loadint64(SB),NOSPLIT,$0-16 JMP runtime∕internal∕atomic·Load64(SB)我们看到,其实是直接调用runtime∕internal∕atomic·Load64​这......
  • Go标准库源码分析: atomic.AddInt64
    atomic.AddInt64介绍原理源码看不到源码解释个勾八原理源码里只有函数doc,但是没有函数实现,但是有一段注释//AddInt64atomicallyaddsdeltato*addrandreturnsthenewvalue.//Considerusingthemoreergonomicandlesserror-prone[Int64.Add]instead/......
  • cuda原子操作
    如果不用原子操作,在进行计算直方图时会发生计算冲突d_b[i]为h_a中数字i有几个下面的代码将h_a全赋值为3,但d_b[3]却为1#include<iostream>#include"cuda_runtime.h"#include"device_launch_parameters.h"#defineN10__global__voidf(int*a,int*b){ intx=blo......