在Java单例模式中,能否通过同步getInstance()方法替代volatile?
单例模式:全同步方法实现 vs 双重检查锁实现
能不能用全同步方法替代双重检查锁(去掉volatile)?
可以。把整个getInstance()设为同步方法后,不需要volatile也能保证单例的正确性,但会带来明显的性能问题。
先给出全同步方法的实现代码:
public class Singleton { private static Singleton instance; private Singleton() {} public synchronized static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } }
两种实现的执行过程差异
1. 锁粒度与性能表现
- 双重检查锁(DCL):只有第一次初始化实例的过程会进入同步块,后续所有调用直接返回已初始化的
instance,完全不需要竞争锁。在高并发场景下,这种实现的性能开销极低。 - 全同步方法:**每次调用
getInstance()**都必须先获取Singleton.class的内置锁,哪怕实例已经初始化完成。大量并发线程会因为等待锁而阻塞,性能损耗非常大。
2. 内存可见性的保障逻辑
- DCL:依赖
volatile关键字的内存屏障特性,禁止instance = new Singleton()这行代码的指令重排,确保对象完全初始化后才会被其他线程读取,同时保证instance变量的读写操作在多线程间的可见性。 - 全同步方法:依赖
synchronized的内置锁机制——线程进入同步方法时,会强制从主内存刷新变量到工作内存;退出同步方法时,会把工作内存的变量写回主内存,同时禁止同步块内的指令重排。这就保证了即使没有volatile,其他线程也能看到完全初始化的instance。
3. 具体执行流程
- DCL流程:
- 线程调用
getInstance(),先无锁检查instance是否为null; - 如果为null,竞争
Singleton.class的锁; - 获取锁后再次检查
instance(防止多个等待锁的线程重复初始化); - 创建实例后释放锁;
- 后续线程调用时,直接读取非null的
instance返回,全程无锁。
- 线程调用
- 全同步方法流程:
- 任何线程调用
getInstance(),必须先获取Singleton.class的锁; - 获取锁后检查
instance是否为null,创建实例或直接返回; - 释放锁,下一个等待锁的线程重复上述步骤。
- 任何线程调用
内容的提问来源于stack exchange,提问作者Soumya Sen
相关产品推荐
相关产品推荐

