Java 原子类怎么用

在日常写代码时,你是不是遇到过这种情况:多个同事同时改同一个Excel表格的单元格,最后统计的总数不对?这就是“多线程抢资源”的问题,放到程序里也是一样——如果多个线程同时改一个共享变量,很容易算出错的结果。Java原子类就是解决这个问题的“Excel锁定工具”,专门帮你在多线程场景下安全改变量。

先搞懂:为什么普通变量会出问题?

举个生活化的例子:你和朋友同时在ATM机上查余额,都是1000元,然后各自取500元。你先取,把余额改成500元;这时候朋友的屏幕还是1000元,他也改成500元。最后ATM里少了1000元,只剩500元,这就是多线程并发的“超卖/超取”问题。

Java 原子类怎么用

普通变量(比如int类型)的自增操作,其实是“读-改-写”三步:先拿旧值,加1,再写回去。如果两个线程同时读旧值,就会出现上面的错误。Java原子类的核心,就是把这三步变成“原子操作”——要么一次性成功,要么全失败,不会被其他线程打断。

原子类怎么用?从“计数器”例子入门

原子类用起来特别简单,不用复杂的锁,直接创建对象调用方法就行。最常用的是AtomicInteger(对应int类型的原子类),我们用它做一个多线程计数器:

`java

import java.util.concurrent.atomic.AtomicInteger;

public class AtomicDemo {

// 原子计数器,初始值0

private static final AtomicInteger COUNT = new AtomicInteger(0);

public static void main(String[] args) throws InterruptedException {

// 10个线程同时给计数器加1,各加1000次

for (int i = 0; i < 10; i++) {

new Thread(() -> {

for (int j = 0; j < 1000; j++) {

// 核心方法:原子性加1,返回更新后的值

COUNT.incrementAndGet();

}

}).start();

}

// 等所有线程跑完,打印结果

Thread.sleep(1000);

System.out.println("最终计数器值:" + COUNT.get());

}

}

`

如果把AtomicInteger换成普通int,大概率会出现“9999”“10001”这种错值,用原子类的话,结果肯定是10000,完美解决并发问题。

常用原子类对应不同场景

不用记太多,常用的原子类就4种,对应不同的变量类型:

  • AtomicInteger:对应普通int,适合计数器、编号生成这类整数场景;
  • AtomicLong:对应long类型,比如统计系统总请求数,避免int不够用;
  • AtomicBoolean:对应boolean,比如做“一次性任务标记”——标记任务是否执行过,保证只有一个线程能设为true;
  • AtomicReference:对应自定义对象,比如要线程安全地更新用户信息,用它比自己写锁省心。

用原子类的注意事项

原子类虽然好用,但不能随便用,记住两个关键点:

第一,它只能保证单个操作的原子性,如果是多个操作组合,还是会出问题。比如你要“先判断当前值是10,再改成20”,不能直接用AtomicInteger的普通方法,得用compareAndSet(CAS方法):

`java

// 只有当前值是10时,才改成20,成功返回true,失败返回false

boolean success = COUNT.compareAndSet(10, 20);

`

这个方法就像你去买奶茶,必须是你点的那杯(当前值)才会给你,不是的话就重新排队,不会搞错。

第二,它适合“轻量并发”,如果是几十上百个线程抢同一个变量,原子类的效率会下降,这时候改用锁更合适,不用所有场景都硬套原子类。

简单来说,Java原子类就是多线程场景下的“安全操作小工具”,不用写复杂锁,用对方法就能解决大部分并发变量的问题,是写高并发Java代码的基础小技巧。

上一篇 C / C++ 编程语言的核心特性与日常编程实践科普