思想:
相比于饿汉模式,懒汉模式实际中的应用更多,因为在系统中,“被用到时再初始化”是更佳的解决方案。
设计思想与饿汉模式类似,同样是持有一个自身的引用,只是将 new 的动作延迟到 getinstance() 方法中执行。
public final class LazySingleton { private static LazySingleton instance; private LazySingleton() { if (instance != null) { throw new IllegalStateException(); } } public static synchronized LazySingleton getInstance() { if (instance == null) { instance = new LazySingleton(); } return instance; } }
- 反射能否打破单例?
对于 LazySingleton,这是个很有趣的问题,虽然我们在私有构造器中增加了 instance==null 的判断,但是由于延迟加载的原因,使得它无法完美地规避反射的入侵。
这涉及到了反射入侵和 getInstance() 方法调用顺序的问题。
如果在调用 getInstance() 方法之前进行反射入侵,那么就会打破单例,反之,可以保证单例。
public class LazySingletonTest { @Test public void testReflectSuccess() throws Exception { Constructor<?> constructor = LazySingleton1.class.getDeclaredConstructor(); constructor.setAccessible(true); LazySingleton1 singleton1 = (LazySingleton1) constructor.newInstance(); LazySingleton1 singleton2 = LazySingleton1.getInstance(); Assert.assertNotSame(singleton1, singleton2); } @Test public void testReflectFailure() throws Exception { LazySingleton1 singleton1 = LazySingleton1.getInstance(); Constructor<?> constructor = LazySingleton1.class.getDeclaredConstructor(); constructor.setAccessible(true); try { LazySingleton1 singleton2 = (LazySingleton1) constructor.newInstance(); Assert.fail(); } catch (Exception e) { // Do nothing, test pass } } }
- 为什么是 synchronized 方法?
因为是延迟加载,考虑到多线程情况,需要对方法同步。
- 同步方法带来的性能问题?
可以使用 synchronized 代码块 + Double-check Locking + volatile 关键字,对 LazySingleton 进行深一步优化,详情见:第003弹:懒汉型单例模式的演变
- 优势?劣势?
优势:延迟加载。
劣势:不能完全屏蔽反射入侵,而且代码较为繁琐。