Java并发三要素:原子性、可见性、有序性

好的,我们来详细解释一下 Java 中的原子性、可见性和有序性。这三个概念是多线程编程中非常重要的基础,它们定义了线程如何安全地访问和修改共享数据。

  1. 原子性

    • 定义:原子性是指一个操作是不可中断的。要么这个操作完全执行完毕,要么完全不执行。它保证了在多线程环境下,当一个线程执行某个操作时,不会被其他线程干扰。
    • 在 Java 中的体现
      • 基本数据类型的读写(除了 longdouble)通常被认为是原子的(虽然 JVM 规范允许实现有例外,但主流 JVM 实现通常保证其原子性)。
      • 对于 longdouble,由于它们是 64 位的,在 32 位 JVM 上,写操作可能被拆分成两个 32 位的操作,这就不是原子的。不过,现代 64 位 JVM 通常能保证其原子性。为了确保可移植性,如果需要对它们进行原子读写,应使用 volatile 声明或通过同步机制保护。
      • i++(读取、修改、写入)这样的复合操作不是原子的,即使 iint 类型。
      • 保证原子性的机制 :使用 synchronized 关键字或 java.util.concurrent.atomic 包下的原子类(如 AtomicInteger, AtomicLong)可以保证操作的原子性。volatile 关键字本身只能保证单个读/写操作的原子性,不能保证像 i++ 这样的复合操作的原子性。
  2. 可见性

    • 定义:可见性是指当一个线程修改了共享变量的值后,其他线程能够立即看到修改后的值。
    • 问题根源:由于现代计算机体系结构,每个线程为了执行效率,可能会将共享变量拷贝一份到自己的工作内存(通常是 CPU 的寄存器或高速缓存)中。线程对变量的操作首先是在自己的工作内存中进行,之后再同步回主内存。这就可能导致一个线程修改了共享变量后,另一个线程看到的仍然是旧值。
    • 在 Java 中的保证机制
      • volatile 关键字:当声明一个变量为 volatile 时,对该变量的写操作会立即刷新回主内存,并且该变量的读取总是从主内存读取(或者确保看到最新的值)。这保证了修改对其他线程的可见性。
      • synchronized 关键字:在释放锁时,会将线程工作内存中的所有共享变量刷新回主内存;在获取锁时,会使线程工作内存中的共享变量无效,从而强制从主内存重新读取。
      • final 关键字:final 修饰的字段在构造器中被正确初始化后(没有发生 this 引用逸出),其值对其他线程是可见的。
  3. 有序性

    • 定义:有序性是指程序执行的顺序按照代码的先后顺序执行。
    • 问题根源:编译器和处理器为了提高性能,可能会对指令进行重排序(Reordering)。重排序在单线程环境下不会影响最终结果(遵循 as-if-serial 语义),但在多线程环境下,可能会破坏程序的逻辑,导致意想不到的结果。
    • 在 Java 中的保证机制
      • volatile 关键字:volatile 除了保证可见性,还通过添加内存屏障来禁止指令重排序。具体来说:
        • volatile 变量之前的操作不会被重排序到写之后。
        • volatile 变量之后的操作不会被重排序到读之前。
      • synchronized 关键字:synchronized 块内的代码,虽然内部可能发生重排序,但由于互斥性,整个 synchronized 块相对于其他 synchronized 块(或非同步代码)的执行顺序是有序的。
      • Happens-Before 原则 :Java 内存模型(JMM)定义了一系列的 Happens-Before 规则,规定了哪些操作必须在哪些操作之前发生,从而天然地保证了某些情况下的有序性(例如,同一个线程内的顺序、锁的释放先于后续获取、volatile 变量的写先于后续读等)。

总结

  • 原子性 :关注操作的不可分割性。synchronized 和原子类可以保证。
  • 可见性 :关注一个线程的修改能否被其他线程及时看到。volatilesynchronized 可以保证。
  • 有序性 :关注指令执行的顺序是否符合预期。volatilesynchronized 通过限制重排序来保证,Happens-Before 原则是其基础。

理解这三个特性对于编写正确、可靠的多线程 Java 程序至关重要。synchronized 关键字可以同时保证原子性、可见性和有序性,但可能会带来性能开销。volatile 关键字可以保证可见性和有序性(限制部分重排序),但不能保证复合操作的原子性。原子类则通过 CAS 操作提供特定操作的原子性保证。

相关推荐
budingxiaomoli4 小时前
Spring IoC &DI
java·spring·ioc·di
Spider Cat 蜘蛛猫4 小时前
Springboot SSO系统设计文档
java·spring boot·后端
未若君雅裁4 小时前
MySQL高可用与扩展-主从复制读写分离分库分表
java·数据库·mysql
学习中.........4 小时前
从扰动函数的变化,感受红黑树带来的性能提升
java
计算机安禾4 小时前
【c++面向对象编程】第24篇:类型转换运算符:自定义隐式转换与explicit
java·c++·算法
weixin199701080165 小时前
【保姆级教程】淘宝/天猫商品详情 API(item_get)接入指南:Python/Java/PHP 调用示例与 JSON 返回值解析
java·python·php
环流_5 小时前
redis核心数据类型在java中的操作
java·数据库·redis
雨辰AI5 小时前
SpringBoot3 项目国产化改造完整流程|从 MySQL 到人大金仓落地
java·数据库·后端·mysql·政务
带刺的坐椅5 小时前
Java 流程编排新范式 Solon Flow:一个引擎,七种节点,覆盖规则/任务/工作流/AI 编排全场景
java·spring·ai·solon·flow
知彼解己6 小时前
Arthas:Java生产环境问题排查利器,从入门到实战
java