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 性能更好
相关推荐
AKA__Zas几秒前
初识多线程(3.0)
java·开发语言·学习方法
小杍随笔8 分钟前
【Rust 工具链管理工具再升级!rust-verse v1.3.1 ~ v1.3.5 最新更新深度解析】
开发语言·后端·rust
北漂人Java13 分钟前
SpringAI-2.Spring AI整合本地模型和云端大模型
java·spring
迹象Kimizhou_blog14 分钟前
国内 IntelliJ IDEA 集成Claude code,调用deepSeek模型实现agent
java·ide·intellij-idea·deepseek·claude code
大数据三康25 分钟前
在spyder进行的遗传算法练习
开发语言·python·算法
百珏25 分钟前
海量人群包存储优化:基于 RoaringBitmap 交换格式与 Redis 分片 Bitmap 的实践
java·后端·架构
风味蘑菇干26 分钟前
IO流(字节流)
java
Vallelonga29 分钟前
Rust 从结构体中取字段的引用
开发语言·rust
社交怪人1 小时前
【球体体积】信息学奥赛一本通C语言解法(题号1030)
c语言·开发语言
froginwe111 小时前
Foundation 顶部导航栏详解
开发语言