You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java懒加载单例:为何用volatile而非方法级synchronized避免无效读取?

关于懒加载单例的两个问题解答

一、为什么要给字段加volatile防止无效数据读取?

在懒加载单例的双重检查锁实现里,instance = new LazySingleton()这个操作并非原子性的,JVM实际会拆成三步执行:

  1. 为对象分配内存空间
  2. 初始化对象的成员变量、执行构造方法
  3. 将instance引用指向分配好的内存地址

如果没有给instance加volatile关键字,JVM可能会对这三步指令进行重排序,变成1→3→2。这时候就会出现问题:当线程A执行完步骤3但还没执行步骤2时,instance已经不为null了,此时线程B进入第一个if (instance == null)判断,会直接返回这个还没完成初始化的instance,也就是读取到了无效的半初始化对象,这就是所谓的“无效数据读取”。

volatile关键字的作用就是禁止JVM对上述指令进行重排序,确保对象完全初始化完成后,才把instance引用指向内存地址;同时它还能保证多线程之间的可见性,一个线程修改instance的值后,其他线程能立刻看到最新的结果,避免因为缓存一致性问题读取到旧值。

二、方法声明加synchronized能否替代代码块同步实现相同效果?

能实现线程安全的单例效果,但两者的性能差异很大:

  • 第二种实现(方法级synchronized):整个getInstance()方法被加锁,每次调用这个方法时,不管instance是否已经初始化,所有线程都必须排队获取锁,并发场景下性能损耗严重,相当于把懒加载变成了串行化操作。
  • 第一种实现(代码块同步+双重检查):只有当instance为null的时候,才会进入同步块抢锁;一旦instance初始化完成,后续调用getInstance()都不需要再进入同步块,直接返回实例,并发性能好很多。

另外,方法级synchronized本身也能保证内存可见性和有序性,所以不需要加volatile也不会出现无效数据读取的问题,但代价是牺牲了并发性能。


内容的提问来源于stack exchange,提问作者kaka

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.15 16:42:14