DXP BLOG
首页
  • JDk
  • Spring系列
  • 微服务
  • Json
  • Netty
  • Bug
  • Mysql
  • Postgresql
  • 达梦
  • activemq
  • rabbitmq
  • rocketmq
  • redis
  • Vue
  • React
  • Angular
  • Javascript
  • Typescript
linux
  • 协议
  • 加解密
  • 分类
  • 标签
  • 归档
Gitee (opens new window)

董新平

一个普普通通的代码程序猿
首页
  • JDk
  • Spring系列
  • 微服务
  • Json
  • Netty
  • Bug
  • Mysql
  • Postgresql
  • 达梦
  • activemq
  • rabbitmq
  • rocketmq
  • redis
  • Vue
  • React
  • Angular
  • Javascript
  • Typescript
linux
  • 协议
  • 加解密
  • 分类
  • 标签
  • 归档
Gitee (opens new window)
  • JDK

    • ThreadPoolExecutor
    • Object类12中方法及作用
    • CountDownLatch
    • 读写锁
    • Atomic
      • 1. 如何使用
        • 1.1 原子更新基本类型
        • 1.2 原子更新数组
        • 1.3 原子更新引用
        • 1.4 原子更新属性
        • 1.5 计数(统计)
  • Spring系列

  • Json

  • JAVA
  • JDK
dongxinping
2023-12-27
目录

Atomic

# Atomic

jdk17

atomic 是 Java 并发编程中的原子类包,全称 java.util.concurrent.atomic, 一共 17 个类,分为五大类: 原子更新基本类型, 原子更新数组, 原子更新引用, 原子更新属性, 计数(统计). 所以的类基本都是基于 CAS(compare and swap) 原理实现的(乐观锁)

# 1. 如何使用

# 1.1 原子更新基本类型

AtomicBoolean, AtomicInteger, AtomicLong, 从名字上可以看出, 分别针对 布尔,整型和长整型的原子操作。

这几个类的使用方式基本一致,分别会提供以下方法:

  1. addAndGet(int delta): 以原子方式将输入的数值与实例中原本的值相加,并返回最后的结果
  2. incrementAndGet():以原子的方式将实例中的原值进行加 1 操作,并返回最终相加后的结果
  3. getAndSet(int newValue):将实例中的值更新为新值,并返回旧值
  4. getAndIncrement():以原子的方式将实例中的原值加 1,返回的是自增前的旧值

# 1.2 原子更新数组

  1. AtomicIntegerArray 原子更新整型数组里的元素
  2. AtomicLongArray 原子更新长整型数组里的元素
  3. AtomicReferenceArray 原子更新引用类型数组里的元素

常见方法列举:

  1. int addAndGet(int i, int delta):以原子方式将输入值与数组中索引 i 的元素相加.
  2. boolean compareAndSet(int i, int expect, int update):如果当前值等于预期值,则以原子方式将数组位置 i 的元素设置成 update 值.
  3. getAndIncrement(int i), getAndDecrement(int i), incrementAndGet(int i), decrementAndGet(int i) 与上面类似, 数组针对的是数组上某一个索引上的操作

# 1.3 原子更新引用

  • AtomicMarkableReference: 通过boolean型的标识来判断数据是否有更改过
  • AtomicReference: 针对引用类型数据的原子操作.
  • AtomicStampedReference: 通过int类型的版本号来判断数据是否有更改过

测试代码

package test;

import java.util.concurrent.atomic.AtomicMarkableReference;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

public class Test1 {

    public record User(String name, int age) {
    }

    private static void reference() {
        User user1 = new User("张三", 23);
        User user2 = new User("李四", 25);
        User user3 = new User("王五", 20);

        //初始化为 user1
        AtomicReference<User> atomicReference = new AtomicReference<>();
        atomicReference.set(user1);

        //把 user2 赋给 atomicReference, 设置成功.
        atomicReference.compareAndSet(user1, user2);
        System.out.println(atomicReference.get());

        //把 user3 赋给 atomicReference, 设置失败.
        atomicReference.compareAndSet(user1, user3);
        System.out.println(atomicReference.get());
    }

    public static void atomicMarkableReference() {
        AtomicMarkableReference<String> markableReference = new AtomicMarkableReference<>("a", false);
        boolean oldMarked = markableReference.isMarked();
        String oldReference = markableReference.getReference();

        System.out.println("初始化之后的标记:" + oldMarked + ", 初始化之后的值:" + oldReference);
        String newReference = "b";
        boolean b = markableReference.compareAndSet(oldReference, newReference, true, false);
        if (!b) {
            System.out.println("Mark不一致,无法修改Reference的值");
        }
        b = markableReference.compareAndSet(oldReference, newReference, false, true);
        if (b) {
            System.out.println("Mark一致,修改reference的值为b");
        }
        System.out.println("修改成功之后的Mark:" + markableReference.isMarked() + ", 修改成功之后的值:" + markableReference.getReference());
    }

    public static void atomicStampedReference() {
        AtomicStampedReference<String> markableReference = new AtomicStampedReference<>("a", 1);
        int stamp = markableReference.getStamp();
        String oldReference = markableReference.getReference();

        System.out.println("初始化之后的标记:" + stamp + ", 初始化之后的值:" + oldReference);
        String newReference = "b";
        boolean b = markableReference.compareAndSet(oldReference, newReference, 2, 2);
        if (!b) {
            System.out.println("stamp不一致,无法修改Reference的值");
        }
        b = markableReference.compareAndSet(oldReference, newReference, 1, 2);
        if (b) {
            System.out.println("stamp一致,修改reference的值为b");
        }
        System.out.println("修改成功之后的Mark:" + markableReference.getStamp() + ", 修改成功之后的值:" + markableReference.getReference());
    }

    public static void main(String[] args) {
        reference();
        System.out.println("====================================================================================");

        atomicMarkableReference();
        System.out.println("====================================================================================");

        atomicStampedReference();
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75

输出

User[name=李四, age=25]
User[name=李四, age=25]
====================================================================================
初始化之后的标记:false, 初始化之后的值:a
Mark不一致,无法修改Reference的值
Mark一致,修改reference的值为b
修改成功之后的Mark:true, 修改成功之后的值:b
====================================================================================
初始化之后的标记:1, 初始化之后的值:a
Mark不一致,无法修改Reference的值
Mark一致,修改reference的值为b
修改成功之后的Mark:2, 修改成功之后的值:b
1
2
3
4
5
6
7
8
9
10
11
12

# 1.4 原子更新属性

AtomicIntegerFieldUpdater, AtomicLongFieldUpdater, AtomicReferenceFieldUpdater, 以原子的方式更新对象的某一个属性.

以更新Integer类似属性为例

package test;

import java.util.concurrent.atomic.AtomicIntegerFieldUpdater;
import java.util.concurrent.atomic.AtomicMarkableReference;
import java.util.concurrent.atomic.AtomicReference;
import java.util.concurrent.atomic.AtomicStampedReference;

/**
 * Test2
 *
 * @author 董新平
 * @version 1.0.0001 2023/12/29 14:48
 * @since 2023/12/29 14:48
 */
public class Test2 {

    public static class User {
        String name;
        // 变更的属性需要采用 volatile 修改
        volatile int age;

        public User(String name, int age) {
            this.name = name;
            this.age = age;
        }
    }

    public static void main(String[] args) {
        AtomicIntegerFieldUpdater<User> fieldUpdater = AtomicIntegerFieldUpdater.newUpdater(User.class, "age");
        User user = new User("张三", 17);
        int age = fieldUpdater.addAndGet(user, 1);
        System.out.println("累计后的值:" + age);
        fieldUpdater.compareAndSet(user, 18, 25);
        System.out.println("设置后的年龄: " + user.age);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36

输出后的结果

累计后的值:18
设置后的年龄: 25
1
2

# 1.5 计数(统计)

Striped64, DoubleAccumulator, DoubleAdder, LongAccumulator, LongAdder, Striped64 是在 java8 中添加用来支持累加器的并发组件,它可以在并发环境下使用来做某种计数,Striped64 的设计思路是在竞争激烈的时候尽量分散竞争,在实现上,Striped64 维护了一个 base Count 和一个 Cell 数组,计数线程会首先试图更新 base 变量,如果成功则退出计数,否则会认为当前竞争是很激烈的,那么就会通过 Cell 数组来分散计数,Striped64 根据线程来计算哈希,然后将不同的线程分散到不同的 Cell 数组的 index 上,然后这个线程的计数内容就会保存在该 Cell 的位置上面,基于这种设计,最后的总计数需要结合 base 以及散落在 Cell 数组中的计数内容.

使用案例

package test;

import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.atomic.LongAccumulator;
import java.util.concurrent.atomic.LongAdder;
import java.util.stream.IntStream;

/**
 * Test3
 *
 * @author 董新平
 * @version 1.0.0001 2023/12/29 14:48
 * @since 2023/12/29 14:48
 */
public class Test3 {

    static final LongAdder adder = new LongAdder();
    static final CountDownLatch count = new CountDownLatch(10);

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            new Thread(() -> {
                adder.increment();
                count.countDown();
            }).start();
        }

        count.await();
        System.out.println(adder.sum());

        System.out.println("==============================================================");
        LongAccumulator accumulator = new LongAccumulator(Long::sum, 0);
        ExecutorService executor = Executors.newFixedThreadPool(8);
        IntStream.range(1, 10).forEach(i -> executor.submit(() -> accumulator.accumulate(i)));

        Thread.sleep(2000);
        System.out.println(accumulator.getThenReset());
    }
}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42

输出结果

10
==============================================================
45
1
2
3
#源码阅读
上次更新: 2023/12/29, 17:39:06
读写锁
Bean创建三级缓存

← 读写锁 Bean创建三级缓存→

最近更新
01
Redis数据类型
01-20
02
编译安装Redis
12-27
03
Windows下Redis安装
11-16
更多文章>
dongxinping | Copyright © 2022-2024 Dongxinping | MIT License
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式