设计模式单例模式:2-懒汉模式
Posted Gerrard_Feng
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式单例模式:2-懒汉模式相关的知识,希望对你有一定的参考价值。
思想:
相比于之前提及的饿汉模式,懒汉模式的实际应用场景更加广泛一些。
因为在系统中,大多数的类都不必在一开始就初始化,在第一次投入使用的时候再初始化就可以了,这样做最大的好处无疑就是节省了大片的内存空间。
设计的思想与饿汉模式类似,同样是持有一个自身的引用,只是将 new 的动作延迟到 getinstance() 方法中执行。
1 public class LazySingleton { 2 3 private static LazySingleton instance; 4 5 private LazySingleton() { 6 7 } 8 9 public static LazySingleton getInstance() { 10 if (instance == null) { 11 instance = new LazySingleton(); 12 } 13 return instance; 14 } 15 16 }
考虑反射:
类似的,可以在 private 的构造器中,增加对 instance 的非空判断,但是这么做,并不能完全阻止反射的入侵。
如果在第一次调用 getInstance() 方法之前,实施反射入侵,那么就会打破单例的情况;反之,如果在第一次调用 getInstance() 方法之后,可以阻止反射的入侵。
1 @Test 2 public void test() throws Exception { 3 Constructor<?> constructor = LazySingleton.class.getDeclaredConstructor(); 4 constructor.setAccessible(true); 5 // 反射入侵成功,必须优先反射 6 LazySingleton singleton3 = (LazySingleton) constructor.newInstance(); 7 LazySingleton singleton1 = LazySingleton.getInstance(); 8 LazySingleton singleton2 = LazySingleton.getInstance(); 9 Assert.assertSame(singleton1, singleton2); 10 Assert.assertNotSame(singleton1, singleton3); 11 }
考虑多线程:
由于在 getInstance() 时,才会初始化对象,那么在高并发的情况下,同时多个线程调用 getInstance() 方法,就可以成功地构造多个对象,从而打破单例。
有一种 DCL 双锁检测机制,就是专门用来处理这种情况的。
为什么必须是双锁,仅仅对方法加上 synchronized 修饰不行吗?
单单使用 synchronized,理论上是可以禁止多线程情况下创建多个实例的。但是还需要考虑另外一种情况:指令重排。
所有的 Java 代码,在 JVM 中最终都会转化为一系列二进制的操作指令,而 JVM 为了优化指令的执行效率,可能会进行一些重新排序,从而使代码并不是以我们所见到的形式自上而下执行。
Java 语言提供了许多禁止指令重排的方式,而在这里,给 instance 加上另一层限制:volatile,则是最好的解决方案。
1 public class LazySingleton { 2 3 private static volatile LazySingleton instance; 4 5 private LazySingleton() { 6 if (instance != null) { 7 throw new IllegalStateException(); 8 } 9 } 10 11 public static synchronized LazySingleton getInstance() { 12 if (instance == null) { 13 instance = new LazySingleton(); 14 } 15 return instance; 16 } 17 18 }
以上是关于设计模式单例模式:2-懒汉模式的主要内容,如果未能解决你的问题,请参考以下文章