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 性能更好
相关推荐
alphageek81 小时前
Matlab linspace函数完全指南:从基础用法到进阶技巧
开发语言·其他·matlab
AI人工智能+电脑小能手1 小时前
【大白话说Java面试题】【Java基础篇】第29题:静态代理和动态代理的区别是什么
java·开发语言·后端·面试·代理模式
善恶怪客1 小时前
Java-数组和可变参数
java·开发语言
小编码上说1 小时前
LSH(局部敏感哈希)分桶,海量数据下的相似性搜索解决方案
java·spring boot·缓存·langchain4j·lsh·局部敏感哈希·ai调用优化
谭欣辰1 小时前
LCS(最长公共子序列)详解
开发语言·c++·算法
RPGMZ2 小时前
RPGMakerMZ 地图存档点制作 标题继续游戏直接读取存档
开发语言·javascript·游戏·游戏引擎·rpgmz·rpgmakermz
计算机_毕业设计2 小时前
java-springboot数字藏品系统 基于 SpringBoot 的区块链数字艺术品交易平台 Java 微服务架构下的加密藏品展示与拍卖系统计算机毕业设计
java·spring boot·课程设计
ONVO ncen2 小时前
Redis6.2.6下载和安装
java
丑八怪大丑2 小时前
JDK8-17新特性
java·开发语言