当前位置: 首页 > ops >正文

Java高频面试之并发编程-13

hello啊,各位观众姥爷们!!!本baby今天又来报道了!哈哈哈哈哈嗝🐶

面试官:详解原子性、可见性、有序性


在并发编程中,原子性(Atomicity)可见性(Visibility)有序性(Ordering) 是确保多线程程序正确性的三大核心特性。它们共同解决了多线程环境下的数据竞争、状态不一致等问题。


1. 原子性(Atomicity)

定义

原子性指一个操作不可分割,要么完全执行成功,要么完全不执行,中间状态对其他线程不可见。

问题场景
  • 复合操作的非原子性:例如 i++ 操作实际分为三步:读取 i、计算 i+1、写回 i。多线程下,多个线程可能交替执行这些步骤,导致最终结果不符合预期。
  • 示例
    int i = 0;
    // 线程1和线程2同时执行以下代码
    i++; // 实际可能丢失部分更新
    
    若两个线程同时执行 i++,期望结果应为 2,但可能得到 1
解决方案
  • 锁机制(如 synchronized):

    synchronized (lock) {i++;
    }
    

    通过互斥锁确保同一时间只有一个线程执行代码块。

  • 原子类(如 AtomicInteger):

    AtomicInteger atomicInt = new AtomicInteger(0);
    atomicInt.incrementAndGet(); // CAS 操作保证原子性
    

    底层基于 CAS(Compare-And-Swap) 实现,无锁且高效。

  • 基本类型的原子读写
    Java 中除 longdouble 外的基本类型(如 intboolean)的读写是原子的。
    注意:32 位 JVM 中 long/double 的非原子性可能导致“写撕裂”(写入半个字)。


2. 可见性(Visibility)

定义

可见性指当一个线程修改共享变量后,其他线程能立即看到修改后的值。

问题场景
  • 工作内存与主内存不一致
    线程操作变量时,可能从工作内存(CPU 缓存)读取副本,而非主内存。未同步时,修改可能对其他线程不可见。
  • 示例
    boolean flag = true;
    // 线程1
    while (flag) { /* ... */ } // 可能死循环,即使线程2修改了flag
    // 线程2
    flag = false;
    
    线程2修改 flag 后,线程1可能无法及时感知。
解决方案
  • volatile 关键字

    volatile boolean flag = true;
    

    volatile 强制所有读写直接操作主内存,并通过内存屏障禁止重排序。

  • 锁机制(如 synchronizedLock):
    锁的释放会强制将工作内存刷新到主内存,锁的获取会清空工作内存,从主内存重新加载变量。

  • final 关键字
    正确构造的对象(无 this 逃逸),其 final 字段的初始化值对所有线程可见。

    final int x = 42; // 其他线程看到x一定是42
    

3. 有序性(Ordering)

定义

有序性指程序执行的顺序按代码的先后顺序执行,但编译器和处理器可能重排序指令以提高性能。

问题场景
  • 指令重排序导致逻辑错误
    例如双重检查锁定(DCL)单例模式中,可能返回未完全初始化的对象。
    // 错误示例
    if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton(); // 可能发生重排序}}
    }
    
    new Singleton() 的步骤可能被重排序为:分配内存 → 返回引用 → 初始化对象。其他线程可能拿到未初始化的实例。
解决方案
  • volatile 关键字

    private static volatile Singleton instance;
    

    volatile 禁止指令重排序,确保对象完全初始化后才赋值给引用。

  • Happens-Before 规则
    Java 内存模型定义的操作顺序规则,包括:

    • 程序顺序规则:单线程内代码顺序执行。
    • 锁规则:解锁先于后续加锁。
    • volatile 规则:写操作先于后续读操作。
    • 传递性:若 A 先于 B,B 先于 C,则 A 先于 C。
  • 内存屏障
    JVM 通过插入内存屏障(如 LoadLoadStoreStore)限制编译器和 CPU 的重排序。


三者的关系与对比

特性定义典型问题解决工具
原子性操作不可分割竞态条件(如 i++ 错误)synchronized、原子类、CAS
可见性修改后其他线程立即可见脏读、死循环volatilesynchronizedfinal
有序性代码顺序执行,禁止重排序对象未初始化、逻辑错误volatile、Happens-Before、内存屏障

实际应用示例

1. 原子性:银行转账
public class Account {private int balance;// 使用 synchronized 保证原子性public synchronized void transfer(Account target, int amount) {if (this.balance >= amount) {this.balance -= amount;target.balance += amount;}}
}
2. 可见性:状态标志
public class Server {private volatile boolean isRunning = true;public void stop() { isRunning = false; }public void serve() {while (isRunning) { /* 处理请求 */ }}
}
3. 有序性:单例模式(DCL)
public class Singleton {private static volatile Singleton instance;public static Singleton getInstance() {if (instance == null) {synchronized (Singleton.class) {if (instance == null) {instance = new Singleton(); // 禁止重排序}}}return instance;}
}

在这里插入图片描述

http://www.xdnf.cn/news/4488.html

相关文章:

  • 奥威BI:AI驱动的智能财务分析革新,重塑企业决策新范式
  • 深入探索 Spark RDD 行动算子:功能解析与实战应用
  • Python基础语法(上)
  • 从图灵机到量子计算:逻辑可视化的终极进化
  • 基于C++实现(控制台)交通咨询系统
  • C语言指针用法详解
  • 切片和边缘计算技术分析报告
  • 【今日三题】跳台阶扩展问题(找规律) / 包含不超过两种字符的最长子串 / 字符串的排列(回溯—全排列)
  • 架设手游使用游戏盾SDK怎么提升网络速度?
  • 【ROS2】Nav2源码之行为树定义、创建、加载
  • 六级阅读———2024.12卷一 仔细阅读2
  • 城楼预约(二):参数逆向分析思路
  • 挑战用豆包教我学Java01天
  • 单地平面6层PCB设计实战:如何兼顾电源与信号完整性?
  • Ubuntu手动安装Consul 的详细步骤
  • 如何选择海外专线网络呢?实现业务覆盖
  • 2025安徽通信施工安全员C证精选练习题
  • 【PostgreSQL数据分析实战:从数据清洗到可视化全流程】6.4 时间序列分析(窗口函数处理时间数据)
  • Vue3项目,用ts写一个方法,生成1到50的随机整数,包含1和50
  • Excel表格怎样导出为csv格式
  • Selenium的driver.get_url 和 手动输入网址, 并点击的操作,有什么不同?
  • WD1016 升压转换器性能特点及应用场景分析
  • C++_MD5算法
  • python+pytest接口自动化测试--日志记录
  • Linux cp 目录A所有内容到目录B
  • 深入探索Linux命名管道(FIFO):原理、实践与高级技巧
  • 【上位机——MFC】对话框
  • C25-数组应用及练习
  • DHCP理解
  • 在scala中,转换算子和行动算子有什么区别