Java并发包下Atomic相关类的使用

简介: 本文介绍了 `java.util.concurrent.atomic` 包下的各类原子类及其使用场景,包括基本类型原子类(如 `AtomicInteger`、`AtomicLong`)、数组类型原子类(如 `AtomicIntegerArray`)、引用类型原子类(如 `AtomicReference`)、对象属性修改原子类(如 `AtomicIntegerFieldUpdater`)以及原子操作增强类(如 `LongAdder` 和 `LongAccumulator`)。同时,详细对比了不同原子类在高并发场景下的性能表现,展示了 `LongAdder` 的高效性。

java.util.concurrent.atomic 分类:

  • 基本类型原子类
  • AtomicInteger
  • AtomicLong
  • AtomicBoolean
  • 数组类型原子类
  • AtomicIntegerArray
  • AtomicLongArray
  • AtomicReferenceArray
  • 引用类型原子类
  • AtomicReference
  • AtomicMarkableReference
  • AtomicStampedReference
  • 对象的属性修改原子类
  • AtomicIntegerFieldUpdater
  • AtomicLongFieldUpdater
  • AtomicReferenceFieldUpdater
  • 原子操作增强类
  • DoubleAccumulator
  • DoubleAdder
  • LongAccumulator
  • LongAdder

AtomicStampedReference 和 AtomicMarkableReference 区别

AtomicStampedReference是携带版本号的引用类型原子类,可以解决ABA问题,记录的是修改过几次

AtomicMarkableReference是将AtomicStampedReference的版本号,简化为true或false,并且只能使用一次

使用示例

  1. 基本类型原子类,以AtomicInteger为例

java

体验AI代码助手

代码解读

复制代码

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;

public class AtomicTest {

    /**
     * 50个线程
     */
    private static final int THREAD_NUM = 50;
    private static final CountDownLatch countDownLatch = new CountDownLatch(THREAD_NUM);

    public static void main(String[] args) throws InterruptedException {
        MyNumber myNumber = new MyNumber();
        for (int i = 0; i < THREAD_NUM; i++) {
            new Thread(() -> {
                try {
                    for (int j = 0; j < 1000; j++) {
                        myNumber.add();
                    }
                } catch (Exception e) {
                    e.printStackTrace();
                } finally {
                    countDownLatch.countDown();
                }
            }).start();
        }
        countDownLatch.await();
        System.out.println("最终结果:" + myNumber.num.get());
    }

}

class MyNumber {
    AtomicInteger num = new AtomicInteger();

    public void add() {
        num.getAndIncrement();
    }
}
  1. 数组类型原子类,以AtomicIntegerArray为例

csharp

体验AI代码助手

代码解读

复制代码

public static void main(String[] args) {
    AtomicIntegerArray atomicIntegerArray1 = new AtomicIntegerArray(5);
    AtomicIntegerArray atomicIntegerArray2 = new AtomicIntegerArray(new int[5]);
    AtomicIntegerArray atomicIntegerArray3 = new AtomicIntegerArray(new int[]{1, 2, 3, 4, 5});

    for (int i = 0; i < atomicIntegerArray3.length(); i++) {
        // 输出 1、2、3、4、5
        System.out.println(atomicIntegerArray3.get(i));
    }

    int temp = 0;
    atomicIntegerArray3.getAndSet(0, 111);
    // 输出 111
    System.out.println(atomicIntegerArray3.get(0));

    // +1
    atomicIntegerArray3.getAndIncrement(1);
    // +1
    atomicIntegerArray3.getAndIncrement(1);
    // 输出 4
    System.out.println(atomicIntegerArray3.get(1));
}
  1. 引用类型原子类,以AtomicReference为例实现一个自旋锁

java

体验AI代码助手

代码解读

复制代码

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicReference;

public class SpinLockDemo {

    AtomicReference<Thread> atomicReference = new AtomicReference<>();

    public void MyLock() {
        System.out.println(Thread.currentThread().getName() + " come in");
        while (!atomicReference.compareAndSet(null, Thread.currentThread())) {

        }
        System.out.println(Thread.currentThread().getName() + " 获取锁成功");
    }

    public void MyUnLock() {
        while (!atomicReference.compareAndSet(Thread.currentThread(), null)) {

        }
        System.out.println(Thread.currentThread().getName() + " 释放锁");
    }

    public static void main(String[] args) {
        SpinLockDemo spinLockDemo = new SpinLockDemo();

        new Thread(() -> {
            // 获取锁
            spinLockDemo.MyLock();
            try {
                // 拿到锁使用3秒钟
                TimeUnit.SECONDS.sleep(3);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
            // 释放锁
            spinLockDemo.MyUnLock();
        }, "t1").start();

        new Thread(() -> {
            spinLockDemo.MyLock();
            spinLockDemo.MyUnLock();
        }, "t2").start();
    }

}

输出结果

体验AI代码助手

代码解读

复制代码

t1 come in
t1 获取锁成功
t2 come in
t1 释放锁
t2 获取锁成功
t2 释放锁
  1. 对象的属性修改原子类,是以一种线程安全的方式操作非线程安全对象内的某些字段
  • 使用要求:更新的对象属性必须使用 public volatile 进行修饰

AtomicIntegerFieldUpdater使用示例

java

体验AI代码助手

代码解读

复制代码

import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;

public class AtomicIntegerFieldUpdaterDemo {

    public static void main(String[] args) throws InterruptedException {
        BankAccount bankAccount = new BankAccount();
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                bankAccount.doAction(bankAccount);
            }).start();
        }
        TimeUnit.SECONDS.sleep(1);
        // 输出1000
        System.out.println(bankAccount.money);
    }

}

class BankAccount {
    public String bankName = "中国银行";
    public volatile int money;

    // 通过反射获取对象
    AtomicIntegerFieldUpdater<BankAccount> updater = AtomicIntegerFieldUpdater.newUpdater(BankAccount.class, "money");

    public void doAction(BankAccount bankAccount) {
        updater.incrementAndGet(bankAccount);
    }
}
  1. 原子操作增强类

LongAdder:只能用来计算加法,且从零开始计算

ini

体验AI代码助手

代码解读

复制代码

LongAdder longAdder = new LongAdder();
longAdder.increment();
longAdder.increment();
longAdder.increment();
// 输出3
System.out.println(longAdder.longValue());

LongAccumulator:提供了自定义的函数操作

ini

体验AI代码助手

代码解读

复制代码

LongAccumulator longAccumulator = new LongAccumulator((x, y) -> x + y, 0);
longAccumulator.accumulate(1);
longAccumulator.accumulate(2);
longAccumulator.accumulate(3);
// 输出6
System.out.println(longAccumulator.longValue());

Atomic高性能对比

示例:使用50个线程,每个线程累加100万次,最后输出结果(类似高并发点赞功能)

ini

体验AI代码助手

代码解读

复制代码

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;

class ClickNumber {
    int number = 0;

    public synchronized void addBySynchronized() {
        number++;
    }

    AtomicInteger atomicInteger = new AtomicInteger(0);

    public void addByAtomicInteger() {
        atomicInteger.incrementAndGet();
    }

    AtomicLong atomicLong = new AtomicLong(0);

    public void addByAtomicLong() {
        atomicLong.incrementAndGet();
    }

    LongAdder longAdder = new LongAdder();

    public void addByLongAdder() {
        longAdder.increment();
    }

    LongAccumulator longAccumulator = new LongAccumulator(Long::sum, 0);

    public void addByLongAccumulator() {
        longAccumulator.accumulate(1);
    }
}

public class LongAdderDemo {

    public static void main(String[] args) throws InterruptedException {
        ClickNumber clickNumber = new ClickNumber();
        long startTime;
        long endTime;

        CountDownLatch countDownLatch1 = new CountDownLatch(50);
        CountDownLatch countDownLatch2 = new CountDownLatch(50);
        CountDownLatch countDownLatch3 = new CountDownLatch(50);
        CountDownLatch countDownLatch4 = new CountDownLatch(50);
        CountDownLatch countDownLatch5 = new CountDownLatch(50);

        startTime = System.currentTimeMillis();
        // 50个线程
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 1000000; j++) {
                        // 每个线程累加100万次
                        clickNumber.addBySynchronized();
                    }
                } finally {
                    countDownLatch1.countDown();
                }
            }, String.valueOf(i)).start();
        }

        countDownLatch1.await();
        endTime = System.currentTimeMillis();
        System.out.println("synchronized 耗时 " + (endTime - startTime) + " 毫秒 " + clickNumber.number);

        startTime = System.currentTimeMillis();
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 1000000; j++) {
                        clickNumber.addByAtomicInteger();
                    }
                } finally {
                    countDownLatch2.countDown();
                }
            }, String.valueOf(i)).start();
        }

        countDownLatch2.await();
        endTime = System.currentTimeMillis();
        System.out.println("AtomicInteger 耗时 " + (endTime - startTime) + " 毫秒 " + clickNumber.atomicInteger.get());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 1000000; j++) {
                        clickNumber.addByAtomicLong();
                    }
                } finally {
                    countDownLatch3.countDown();
                }
            }, String.valueOf(i)).start();
        }

        countDownLatch3.await();
        endTime = System.currentTimeMillis();
        System.out.println("AtomicLong 耗时 " + (endTime - startTime) + " 毫秒 " + clickNumber.atomicLong.get());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 1000000; j++) {
                        clickNumber.addByLongAdder();
                    }
                } finally {
                    countDownLatch4.countDown();
                }
            }, String.valueOf(i)).start();
        }

        countDownLatch4.await();
        endTime = System.currentTimeMillis();
        System.out.println("LongAdder 耗时 " + (endTime - startTime) + " 毫秒 " + clickNumber.longAdder.longValue());

        startTime = System.currentTimeMillis();
        for (int i = 1; i <= 50; i++) {
            new Thread(() -> {
                try {
                    for (int j = 1; j <= 1000000; j++) {
                        clickNumber.addByLongAccumulator();
                    }
                } finally {
                    countDownLatch5.countDown();
                }
            }, String.valueOf(i)).start();
        }

        countDownLatch5.await();
        endTime = System.currentTimeMillis();
        System.out.println("LongAccumulator 耗时 " + (endTime - startTime) + " 毫秒 " + clickNumber.longAccumulator.longValue());
    }

}

从输出结果可见,LongAdder性能明显高于其它的

arduino

体验AI代码助手

代码解读

复制代码

synchronized 耗时 941 毫秒 50000000
AtomicInteger 耗时 980 毫秒 50000000
AtomicLong 耗时 984 毫秒 50000000
LongAdder 耗时 153 毫秒 50000000
LongAccumulator 耗时 262 毫秒 50000000


转载来源:https://juejin.cn/post/6993251728321675301

相关文章
|
4月前
|
Java 编译器 API
Java 密封类:精细化控制继承关系
Java 密封类:精细化控制继承关系
323 83
|
2月前
|
安全 Java 数据建模
Java记录类:简化数据载体的新选择
Java记录类:简化数据载体的新选择
209 101
|
2月前
|
安全 Java 开发者
Java记录类:简化数据载体的新方式
Java记录类:简化数据载体的新方式
268 100
|
5月前
|
IDE Java 数据挖掘
Java 基础类从入门到精通实操指南
这份指南专注于**Java 17+**的新特性和基础类库的现代化用法,涵盖开发环境配置、数据类型增强(如文本块)、字符串与集合处理进阶、异常改进(如密封类)、IO操作及实战案例。通过具体代码示例,如CSV数据分析工具,帮助开发者掌握高效编程技巧。同时提供性能优化建议和常用第三方库推荐,适合从入门到精通的Java学习者。资源链接:[点此下载](https://pan.quark.cn/s/14fcf913bae6)。
214 36
|
3月前
|
安全 IDE Java
Java记录类型(Record):简化数据载体类
Java记录类型(Record):简化数据载体类
362 120
|
28天前
|
存储 Java 索引
用Java语言实现一个自定义的ArrayList类
自定义MyArrayList类模拟Java ArrayList核心功能,支持泛型、动态扩容(1.5倍)、增删改查及越界检查,底层用Object数组实现,适合学习动态数组原理。
79 4
|
1月前
|
IDE JavaScript Java
在Java 11中,如何处理被弃用的类或接口?
在Java 11中,如何处理被弃用的类或接口?
132 5
|
1月前
|
JSON 网络协议 安全
【Java】(10)进程与线程的关系、Tread类;讲解基本线程安全、网络编程内容;JSON序列化与反序列化
几乎所有的操作系统都支持进程的概念,进程是处于运行过程中的程序,并且具有一定的独立功能,进程是系统进行资源分配和调度的一个独立单位一般而言,进程包含如下三个特征。独立性动态性并发性。
114 1
|
1月前
|
Java Go 开发工具
【Java】(8)正则表达式的使用与常用类分享
正则表达式定义了字符串的模式。正则表达式并不仅限于某一种语言,但是在每种语言中有细微的差别。
174 1
|
1月前
|
存储 Java 程序员
【Java】(6)全方面带你了解Java里的日期与时间内容,介绍 Calendar、GregorianCalendar、Date类
java.util 包提供了 Date 类来封装当前的日期和时间。Date 类提供两个构造函数来实例化 Date 对象。第一个构造函数使用当前日期和时间来初始化对象。Date( )第二个构造函数接收一个参数,该参数是从1970年1月1日起的毫秒数。
124 1