首页 > 其他分享 >【JUC】原子操作类

【JUC】原子操作类

时间:2022-12-15 16:36:31浏览次数:29  
标签:JUC name int 更新 原子 操作 array public

目录

1、什么是原子操作类

原子性:无论有多少个操作,只要我们将这部分操作通过加锁的形式进行锁定,那么就可以视为一个原子性操作。原子性操作 = 线程安全。

原子操作类:提供了可以保证我们线程安全的类。可以直接使用。

当程序更新一个变量时,如果多线程同时更新这个变量,可能得到期望之外的值,比如变量i=1,A线程更新i+1,B线程也更新i+1,经过两个线程操作之后可能i不等于3,而是等于2(1.加锁,2. 定义我们的i变量为volatile形式。3. 可以使用我们的一个原子操作类。)。因为A和B线程在更新变量i的时候拿到的i都是1,这就是线程不安全的更新操作,通常我们会使用synchronized来解决这个问题,synchronized会保证多线程不会同时更新变量i。

而Java从JDK 1.5开始提供了java.util.concurrent.atomic包(以下简称Atomic包),这个包中的原子操作类提供了一种用法简单、性能高效(CAS)、线程安全地更新一个变量的方式。

因为变量的类型有很多种,所以在Atomic包里一共提供了13个类,属于4种类型的原子更新方式,分别是原子更新基本类型、原子更新数组、原子更新引用和原子更新属性(字段)。Atomic包里的类基本都是使用Unsafe实现的包装类。

2、原子更新基本类型

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

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

示例代码:BaseAtomicTest.java

import java.util.concurrent.atomic.AtomicInteger;

public class BaseAtomicTest {
    static AtomicInteger ai = new AtomicInteger(0);
    public static void main(String[] args) throws InterruptedException {
        Thread a = new Thread(() -> {
            for(int i = 0; i < 5 ; i++) {
                ai.incrementAndGet(); //返回的增加1之后的值
            }
        });
        Thread b = new Thread(() -> {
            for(int i = 0; i < 7 ; i++) {
                ai.incrementAndGet(); //返回的增加1之后的值
            }
        });
        a.start();
        b.start();
        Thread.sleep(100); //为了保障 a b线程执行完毕之后,才继续执行后续的代码、
        System.out.println(ai.get()); // 获取ai的值
    }
}

3、原子更新数据类型

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

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

AtomicIntegerArray类主要是提供原子的方式更新数组里的整型,

其常用方法如下。

  • int addAndGet(int i,int delta):以原子方式将输入值与数组中索引i的元素相加。
  • boolean compareAndSet(int i,int expect,int update):如果当前值等于预期值,则以原子方式将数组位置i的元素设置成update值。

示例代码:ArrayAtomicTest.java

import java.util.concurrent.atomic.AtomicIntegerArray;

public class ArrayAtomicTest {
    static int[] array = new int[2];
    public static void main(String[] args) {
        array[0] = 10;
        array[1] = 20;

        AtomicIntegerArray aia = new AtomicIntegerArray(array);
        aia.compareAndSet(0, 10, 11);
        System.out.println(aia.get(0)); // 11
        System.out.println(array[0]);// 10
        //我们的数组的原子操作类,只是复制了我们之前的array的对象,并不会
        //在操作中改变原有array的值。
        //原始的array只作为一个入参。真正保障我们的原子性的还是我们的AtomicIntegerArray
        /**
        public AtomicIntegerArray(int[] array) {
        // Visibility guaranteed by final field guarantees
        this.array = array.clone();
    }
        */
        //备份的一份数据。
    }
}

4、原子更新引用类型

原子更新基本类型的AtomicInteger,只能更新一个变量,如果要原子更新多个变量,就需要使用这个原子更新引用类型提供的类。我们介绍一下两个常用类:

  • AtomicReference:原子更新引用类型。
  • AtomicReferenceFieldUpdater:原子更新引用类型里的字段。

示例代码:ReferenceAtomic.java

import java.util.concurrent.atomic.AtomicReference;

public class ReferenceAtomic {
    public static void main(String[] args) {
        AtomicReference<User> ar = new AtomicReference<>();
        User oldUser = new User("T",11);
        ar.set(oldUser);

        ar.compareAndSet(oldUser, new User("F",12));
        System.out.println(ar.get());
    }

    static class User {
        private String name;
        private Integer age;
        public User(String name, Integer age) {
            this.name = name;
            this.age = age;
        }
        @Override
        public String toString(){
            return name + ":" + age;
        }
    }
}

总结:对于原子更新数组类和原子更新对象引用类,他们实质上就是将我们的数组和对象的外层进行了一次 Atomic 的封装。所以,在进行替换的时候,其实是外层的这个封装保证了原子性。
想想我们的原子更新基本类型如:Integer。其实Integer不也是一个对象吗?也是在他的外层进行了一次封装。

5、原子更新字段类

如果需原子地更新某个类里的某个字段时,就需要使用原子更新字段类,Atomic包提供了以下3个类进行原子字段更新。

  • AtomicIntegerFieldUpdater:原子更新整型的字段的更新器。
  • AtomicLongFieldUpdater:原子更新长整型字段的更新器
  • AtomicStampedReference:原子更新带有版本号的引用类型。该类将整数值与引用关联起来,可用于原子的更新数据和数据的版本号,可以解决使用CAS进行原子更新时可能出现的ABA问题。

要想原子地更新字段类需要两步。

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

以上3个类提供的方法几乎一样,所以本节仅以AstomicIntegerFieldUpdater为例进行讲解

示例代码:FieldAtomicTest.java

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class FieldAtomicTest {
    // 创建原子更新器,并设置需要更新的对象类和对象的属性
    private static AtomicIntegerFieldUpdater<User> a = AtomicIntegerFieldUpdater.
            newUpdater(User.class, "old");
    public static void main(String[] args) {
        User conan = new User("conan", 10);
        System.out.println(a.getAndIncrement(conan));// 10 该方法先获取老的值后+1
        System.out.println(a.get(conan));// 11
    }
    public static class User {
        private String name;
        public volatile int old;
        public User(String name, int old) {
            this.name = name;
            this.old = old;
        }
        public String getName() {
            return name;
        }
        public int getOld() {
            return old;
        }
    }

}

输出:

10 
11

标签:JUC,name,int,更新,原子,操作,array,public
From: https://www.cnblogs.com/DarkSki/p/16985352.html

相关文章

  • 【JUC】Lock全解读
    目录0、什么是Lock锁1、自定义Lock锁1.1SelfLock.java1.2测试代码:SelfLockTest.java2、Lock锁的由来及特性及API2.1Lock锁概述2.2Lock特性2.3LockAPI3、AQS底层原......
  • 【JUC】进程和线程
    目录一、进程1、概述2、进程和线程3、并发与并行4、同步与异步5、新线程的创建5.1使用Thread5.2使用Runnable配合Thread5.3FutureTask配合Thread6、自定义线程类的创建6......
  • HDFS基本操作
    #显示根目录/下的文件和子目录,绝对路径hadoopfs-ls/#新建文件夹,绝对路径hadoopfs-mkdir/hello#上传文件hadoopfs-puthello.txt/hello/#下载文件had......
  • 【JUC】并发编程初探
    目录1、Java——天生的多线程1.1main:主线程1.2ReferenceHandle1.3Finalizer1.4SignalDispatcher1.5AttachListenner1.6MonitorCtrl-Break1.7线程1.7.1查看线程......
  • ORM执行SQL语句 、神奇的双下划线查询 、ORM外键字段的创建 、外键字段相关操作 、ORM
    目录ORM执行SQL语句神奇的双下划线查询ORM外键字段的创建外键字段相关操作ORM跨表查询基于对象的跨表查询基于上下划线的跨表查询进阶操作ORM执行SQL语句有时候ORM的操作......
  • RTL8380M/82M管理型交换机系统软件操作指南四:QoS/服务质量
    接下来对QoS进行详细的描述,主要包括以下七大内容:QoS概述、功能简介、拥塞管理、策略分类、调度方式、优先级映射配置、QoS端口配置.1.1QoS概述QoS(QualityofService,服务......
  • 3.2 Docker 容器操作命令 1. 容器生成
    在本地有了镜像之后(默认安装Docker后,会自带初始镜像,可通过Dockerimages命令进行查看),开发者就可以使用镜像生成容器,具体命令如下:dockerrun-d-p8888:8080--nam......
  • 3. Docker 命令操作 3.1 Docker 镜像操作命令
    开发者在装有Docker的机器上可以使用一些命令,进行镜像的管理。命令如下。1.镜像的查看使用以下命令可以查看本地镜像列表,展示结果如图7所示。其中IMAGE_ID为镜像......
  • Canvas学习笔记(五)文本操作
    简介在Canvas中,文本操作的常用方法有:方法说明fillText()绘制实心文本strokeText()绘制空心文本measureText()获取文本长度在Canvas中,文本操作的常......
  • python-docx操作word文档详解
    案例官网地址:https://python-docx.readthedocs.io/en/latest/pipinstallpython-docxfromdocximportDocumentfromdocx.sharedimportInchesdocument=Docum......