导读:本期聚焦于小伙伴创作的《在Java中什么是原子性?一文解析Java原子操作语义与实现原理》,敬请观看详情。为什么i++在多线程下会得到错误结果?根本原因在于该操作不具备原子性。Java原子性指某个操作在执行期间不可被线程调度机制中断,要么全部完成要么完全不发生。基础类型的读写通常具备原子性,但复合操作如自增、检查再更新会拆分为多步,产生竞态。JVM通过synchronized与Lock保证代码块原子性,java.util.concurrent.atomic包则提供无锁原子类,借助底层CAS指令实现高效线程安全。理解原子性与可见性、有序性的区别,才能正确选用同步方案避免数据不一致。

在并发编程中,原子性是Java内存模型定义的三个核心特性之一。它描述了一个操作或者多个操作在执行过程中,是否能被其他线程观察到中间状态。如果一个操作具备原子性,那么从其他线程的视角来看,这个操作要么还没有开始,要么已经彻底结束,绝不会看到执行到一半的结果。比如对一个int类型的变量进行赋值,在Java中通常是原子的;但像i++这样的自增,实际上包含了读取、加一、写回三个步骤,在多线程环境下就可能被交错执行,导致丢失更新。

在Java中什么是原子性?一文解析Java原子操作语义与实现原理

从内存模型看Java原子操作的基础语义

Java语言规范规定,除了long和double之外的所有基本类型(boolean、byte、short、char、int、float)的读写操作都是原子的。这意味着一个线程写入一个int变量时,另一个线程读取该变量不会读到只更新了一半的位模式。对于long和double,在32位JVM上,一次赋值可能被拆成两次32位写入,因此规范允许非原子处理,但现代商用虚拟机基本都将其实现为原子操作。这种由虚拟机保障的原子性,仅限于单一变量的简单读写,不涵盖任何逻辑复合动作。

当我们讨论原子性的语义边界时,必须区分「虚拟机保障的原子性」与「程序员需要的原子性」。业务代码中往往需要「判断余额是否充足,然后扣减」这种复合动作整体不可拆分,而单纯的变量读写原子性无法覆盖此类需求。这也是为什么很多初学者认为「int赋值是原子的就线程安全了」,结果在if-then-update逻辑里依然写出了并发bug。原子操作语义的真正价值,在于为上层同步手段提供正确性基准。

下面的代码展示了非原子复合操作带来的典型问题。两个线程各执行一千次自增,理论上结果应为两千,但实际运行经常小于该值。

public class AtomicDemo {
    private int count = 0;

    public void increment() {
        count++; // 非原子:读-改-写
    }

    public static void main(String[] args) throws InterruptedException {
        AtomicDemo demo = new AtomicDemo();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) demo.increment();
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) demo.increment();
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("最终count=" + demo.count);
    }
}

使用锁机制与原子类保证复合操作原子性

要让复合操作具备原子性,最直观的方式是使用synchronized关键字。它通过对对象监视器加锁,确保同一时刻只有一个线程能进入同步块,从而将读改写过程封装为对外不可分割的整体。这种方案语义清晰、不易出错,但在高并发且临界区较短时,线程阻塞与唤醒的上下文切换开销会比较明显。另一类方案是显式Lock接口,如ReentrantLock,它提供了更灵活的加锁中断与超时能力,但同样属于阻塞式同步。

从JDK 5开始,java.util.concurrent.atomic包提供了一系列无锁原子类,例如AtomicInteger、AtomicLong、AtomicReference。它们底层依赖处理器的CAS(Compare-And-Swap)指令,以乐观锁思路实现原子更新:线程先读取旧值,计算新值,再尝试用CAS将旧值替换为新值,若期间被其他线程改动则重试。这样可以在不阻塞线程的前提下保证复合操作的原子性,非常适合计数器、状态标志等场景。

下面是用AtomicInteger改写后的安全计数器,无论多少线程并发自增,最终结果都准确等于两千。

import java.util.concurrent.atomic.AtomicInteger;

public class SafeAtomicDemo {
    private AtomicInteger count = new AtomicInteger(0);

    public void increment() {
        count.incrementAndGet(); // 原子自增
    }

    public static void main(String[] args) throws InterruptedException {
        SafeAtomicDemo demo = new SafeAtomicDemo();
        Thread t1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) demo.increment();
        });
        Thread t2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) demo.increment();
        });
        t1.start();
        t2.start();
        t1.join();
        t2.join();
        System.out.println("最终count=" + demo.count.get());
    }
}

原子性与可见性、volatile的协作关系

很多开发者容易混淆原子性与可见性。原子性解决的是操作是否可被中断的问题,可见性解决的是一个线程的修改何时对其他线程可见的问题。即便某个操作是原子的,如果缺乏可见性保障,其他线程也可能一直读到旧值。volatile关键字保证了可见性与禁止指令重排序,但不保证复合操作的原子性。例如volatile int i,执行i++依然不是线程安全的,因为读和写之间仍可能被插入其他线程的操作。

在实际设计中,常常需要将原子类或锁与volatile配合使用。比如一个生产者线程通过AtomicBoolean的compareAndSet来原子地翻转状态,而消费者线程用volatile boolean变量做快速失败判断,避免频繁CAS竞争。理解三者差异有助于写出既正确又高效的并发代码:简单标志位可用volatile;复合更新用原子类或锁;跨变量的不变性约束则必须用锁或更高级的并发容器来维持整体一致性。

下面的表格总结了不同同步手段在原子性保障上的特征,便于在方案选型时快速对照。

手段是否保证原子性是否阻塞适用场景
普通变量读写基本类型单写单读是无共享可变状态
volatile变量仅单一读写是状态标志、轻量通知
synchronized是(代码块级)复杂复合操作、多变量约束
Atomic原子类是(方法级CAS)计数器、无锁更新

综合来看,Java原子性并非一个单一语法,而是一组由内存模型、虚拟机实现与并发库共同支撑的语义保证。只有理清从基础读写原子性到复合操作原子化的路径,才能在并发程序中少踩坑、写出可维护的线程安全代码。

Java原子性原子操作volatile修改时间:2026-08-15 18:20:18

免责声明:​ 已尽一切努力确保本网站所含信息的准确性。网站内容多为原创整理与精心编撰,观点力求客观中立。本站旨在免费分享,内容仅供个人学习、研究或参考使用。若引用了第三方作品,版权归原作者所有。如内容涉及您的权益,请联系我们处理。
内容垂直聚焦
专注技术核心技术栏目,确保每篇文章深度聚焦于实用技能。从代码技巧到架构设计,为用户提供无干扰的纯技术知识沉淀,精准满足专业提升需求。
知识结构清晰
覆盖从开发到部署的全链路。AI、前端、编程、数据库、服务器、建站、系统层层递进,构建清晰学习路径,帮助用户系统化掌握开发与运维所需的核心技术。
深度技术解析
拒绝泛泛而谈,深入技术细节与实践难点。无论是数据库优化还是服务器配置,均结合真实场景与代码示例进行剖析,致力于提供可直接应用于工作的解决方案。
专业领域覆盖
精准对应开发生命周期。从前端界面到后端编程,从数据库操作到服务器运维,形成完整闭环,一站式满足全栈工程师和运维人员的技术需求。
即学即用高效
内容强调实操性,步骤清晰、代码完整。用户可根据教程直接复现和应用于自身项目,显著缩短从学习到实践的距离,快速解决开发中的具体问题。
持续更新保障
专注既定技术方向进行长期、稳定的内容输出。确保各栏目技术文章持续更新迭代,紧跟主流技术发展趋势,为用户提供经久不衰的学习价值。