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 性能更好
相关推荐
TELL52127 分钟前
selenium webdriver 第二次初始化的异常
开发语言·python
动词ing1 小时前
【C语言】自定义函数+指针入门
c语言·开发语言·算法
重生之后端学习1 小时前
283. 移动零[简单]✅
开发语言·数据结构·算法·leetcode·职场和发展
吠品1 小时前
Wine 在 Linux 上运行 Windows 软件完整指南
java·linux·服务器
动词ing2 小时前
【C语言】结构体+文件基础
c语言·开发语言·数据结构
caimouse2 小时前
ReactOS 窗口系统分析(7):分层窗口与绘制辅助 — layered.c + draw.c
c语言·开发语言
亚历克斯神3 小时前
智能搜索系统的升级复盘——从 Elasticsearch 到混合检索的检索质量提升
java·spring·微服务
丰锋ff3 小时前
基于 Qt 的智慧社区物业管理系统
开发语言·qt
夜不会漫长3 小时前
C++入门(1)
开发语言·c++·算法
大鹏说大话3 小时前
从爬虫到决策引擎:大数据下自媒体如何用Python挖掘用户痛点
开发语言·爬虫·python