设计模式单例模式: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     }
JUnit

 

考虑多线程:

 

  由于在 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-懒汉模式的主要内容,如果未能解决你的问题,请参考以下文章

[设计模式]单例模式(懒汉式,饿汉式)

单例模式 (饿汉懒汉)

单例模式 (饿汉懒汉)

详解单例模式

单例模式(饿汉方式懒汉方式)

单例模式(饿汉方式懒汉方式)