Java 并发中的原子类

1. 为什么需要原子类

多线程环境下,有个常见问题:

java 复制代码
public class Counter {
    private int count = 0;

    public void increment() {
        count++;
    }
}

count++ 看似一行代码,实际分三步:

  1. 读取 count 的值
  2. 加 1
  3. 写回 count

多线程下可能发生:

复制代码
线程A:读取 count=0
线程B:读取 count=0
线程A:count=1,写回
线程B:count=1,写回   // 线程A的结果被覆盖了

最终 count=1,但实际应该等于 2。

2. 解决办法

synchronized 加锁

java 复制代码
public synchronized void increment() {
    count++;
}

缺点:性能差,每次只能一个线程进入。

原子类

java 复制代码
private AtomicInteger count = new AtomicInteger(0);

public void increment() {
    count.incrementAndGet();
}

一行搞定,性能比 synchronized 高很多。

3. 常用原子类

AtomicInteger

java 复制代码
AtomicInteger count = new AtomicInteger(0);

// 加1
count.incrementAndGet();  // ++i
count.getAndIncrement();  // i++

// 减1
count.decrementAndGet();  // --i
count.getAndDecrement();  // i--

// 加任意值
count.addAndGet(5);

// 获取值
int value = count.get();

// 设置值
count.set(100);

// CAS 操作
count.compareAndSet(100, 200);  // 如果当前值是100,则设置为200

AtomicLong

和 AtomicInteger 用法一样,只是类型不同:

java 复制代码
AtomicLong count = new AtomicLong(0);
count.incrementAndGet();

AtomicBoolean

java 复制代码
AtomicBoolean flag = new AtomicBoolean(false);

// 设为 true
flag.set(true);

// CAS 操作
flag.compareAndSet(false, true);

AtomicReference

用来原子更新对象:

java 复制代码
AtomicReference<User> userRef = new AtomicReference<>();

User user1 = new User("张三", 20);
User user2 = new User("李四", 25);

userRef.set(user1);
userRef.compareAndSet(user1, user2);  // 如果当前是 user1,则改为 user2

4. 数组原子类

java 复制代码
// 数组中某个元素原子操作
AtomicIntegerArray arr = new AtomicIntegerArray(new int[]{1, 2, 3});

arr.getAndAdd(0, 10);  // 第一个元素加10
int value = arr.get(0);  // 获取第一个元素

5. 累加器

jdk8 新增的,比 AtomicInteger 性能更高,专门用于累加场景:

java 复制代码
LongAdder count = new LongAdder();

count.add(1);
count.increment();
long value = count.sum();

内部原理是把一个值拆成多个 Cell,减少竞争。适合高并发场景下的累加。

java 复制代码
// AtomicInteger vs LongAdder
// 低并发:两者差不多
// 高并发:LongAdder 性能更好

6. 实战示例

计数器

java 复制代码
public class PageViewCounter {
    private AtomicInteger count = new AtomicInteger(0);

    public void addView() {
        count.incrementAndGet();
    }

    public int getViews() {
        return count.get();
    }
}

防止重复提交

java 复制代码
public class SubmitService {
    private AtomicBoolean submitting = new AtomicBoolean(false);

    public void submit() {
        // 如果已经在提交中,直接返回
        if (!submitting.compareAndSet(false, true)) {
            return;
        }

        try {
            // 业务逻辑
            doSubmit();
        } finally {
            submitting.set(false);
        }
    }
}

配置更新

java 复制代码
public class ConfigHolder {
    private AtomicReference<Config> configRef = new AtomicReference<>();

    public void updateConfig(Config newConfig) {
        configRef.updateAndGet(old -> newConfig);
    }

    public Config getConfig() {
        return configRef.get();
    }
}

7. 总结

类 用途
AtomicInteger 整数原子操作
AtomicLong 长整数原子操作
AtomicBoolean 布尔原子操作
AtomicReference 对象引用原子操作
AtomicIntegerArray 整数数组原子操作
LongAdder 高并发累加,比 AtomicLong 性能更好

什么时候用:

  • 需要保证多线程安全地修改一个值时
  • 简单计数、状态标记等场景
  • 比 synchronized 性能更好
相关推荐
caoerzhong16 分钟前
中小企业上 WMS 该先上哪几块:JeeWMS 开源 Java 仓库管理系统的分批上线清单
java·python·开源
WL学习笔记1 小时前
日期类(C++)
开发语言·c++·日期类实现
【JAVA】玩家1 小时前
Spring核心原理全解析:从零到生产实战
java·后端·spring
Wang's Blog2 小时前
Java框架 SpringCloud 快速入门: 实现 Feign 最佳实践(抽取方式)
java·spring cloud
谢亮_vipxieliang2 小时前
Go map与结构体的正确使用
开发语言·golang·哈希算法
H.莓飛2 小时前
【C++】命名空间、缺省参数、函数重载与引用
linux·开发语言·c++·visual studio
xxwl5852 小时前
数据结构知识点和代码实现总结(C语言实现)
c语言·开发语言·数据结构
MandalaO_O2 小时前
IDEA 开发(快捷键 + 调试 + 序列化)
java·ide·intellij-idea
xiaoqiMikko3 小时前
JVM 线上排查实战(七):jps 看不见它、jstack 连不上它,可它明明活得好好的
java·jvm
狼爷4 小时前
Rust/Go/Java/Python/PHP 大比拼:负载下后端框架到底差多少?
java·后端·编程语言