设计模式之单例模式

Posted themonster

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了设计模式之单例模式相关的知识,希望对你有一定的参考价值。

“学习的路上没有捷径,我是这样认为的...”

 

定义:确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。

所以需要隐藏构造方法。

属于创建型模式【有待查阅】

 

单例模式有点:

  1. 内存中只有一个实例,减少内存开销

  2. 避免对资源的多重占用 【不是很理解】

  3. 严格控制访问

 

单例模式的几种实现以及分析

1.【饿汉式】

 1 public class HungrySingleton {
 2     private static final HungrySingleton hungrySingleton  = new HungrySingleton();
 3 
 4     private HungrySingleton(){
 5         throw new RuntimeException("Don‘t Create");//防止通过其他途径去实例化对象
 6     }
 7     public HungrySingleton getInstance(){
 8         return hungrySingleton;
 9     }
10 }

特点:1. 线程安全,

   2. 在类加载的时候,就会将该类实例化,即便没有使用也会加载,导致资源浪费。

 

2.【懒汉式】

 1 public class LazySingleton {
 2     private LazySingleton(){
 3         throw new RuntimeException("Don‘t Create");//防止通过其他途径去实例化对象
 4     }
 5     private volatile static LazySingleton lazySingleton;
 6 
 7     public static LazySingleton getInstance(){
 8         if(lazySingleton==null) {   //该层判断只是优化让所有线程进来先进行判断,
 9                                     //如果没有该判断,当该对象已经实例化了,
10                                     //但是【每个】线程进来都会等待同步锁,导致效率低
11             synchronized (LazySingleton.class) {
12                 if (lazySingleton == null) { //该层判断只是为了当多个线程都走过了第一个判断
13                                              // 第一个线程已经初始化实例了
14                                              //防止后来的线程再次创建
15                     lazySingleton = new LazySingleton();
16                 }
17             }
18         }
19         return lazySingleton;
20     }
21 }

 

特点:双重校验保证了效率和线程安全

注意:使用 【volatile】关键字的原因

第13行,new LazySingleton(),new一个对象,有4个步骤

  1. 分配内存空间。

  2. 初始化实例。

  3. 返回地址引用。

  cpu的指令重排序,可能会将2.3执行顺序打乱

接下来,通过一段伪代码来看下

 1 instance = new SingletonDemo();//可以分为以下3步完成(伪代码)
 2 memory = allocate();    // 1.分配内存对象空间
 3 instance = (memory);   // 2.初始化对象
 4 instance = memory;     // 3.设置instance指向刚分配的内存地址,此时instance != null
 5 //由于步骤2和步骤3不存在数据依赖关系,而且无论重排前还是重排后程序的执行结果在单线程中并没有改变
 6 //因此这种重排优化是允许的。
 7 memory = allocate();    // 1.分配内存对象空间
 8 instance = memory;     // 3.设置instance指向刚分配的内存地址,此时instance != null,
 9                         // 【但是对象初始化没有完成,就相当于这个地址空间内没有实际的值。】
10 instance = (memory);   // 2.初始化对象

 

volatile 可以禁止CPU指令重排序,从而避免这个问题。

 

3.静态内部类单例模式

 1 public class LazyInnerClassSingleton {
 2     private LazyInnerClassSingleton(){
 3         throw new RuntimeException("Don‘t Create");//防止通过其他途径去实例化对象
 4     }
 5     public static final LazyInnerClassSingleton getInstance(){
 6         return LazyHolder.LAZY;
 7     }
 8     private static class LazyHolder{
 9         private static final LazyInnerClassSingleton LAZY = new LazyInnerClassSingleton();
10     }
11 }

特点:只有在调用getInstance的方法的同时,才会去初始化,并且只有一个线程可以活得对象的初始化锁。保证线程安全。【最优写法】

【对象初始化锁】还有待查询学习。

 

4. 枚举实现单例模式

1 public enum EnumSingleton {
2     INSTANCE;
3 
4     public static EnumSingleton getInstance(){
5         return INSTANCE;
6     }
7 }

特点:利用枚举的特性,让JVM来帮我们保证线程安全和单一实例的问题。除此之外,写法还特别简单。

 

【但是】

使用序列化,反序列化仍然可以将单例模式破坏

 1 public class SingletonTest {
 2     public static void main(String[] args) throws Exception{
 3         LazySingleton s1 = null;
 4         LazySingleton s = LazySingleton.getInstance();
 5         FileOutputStream fos = new FileOutputStream("a.txt");
 6         ObjectOutputStream oos = new ObjectOutputStream(fos);
 7         oos.writeObject(s);
 8         oos.flush();
 9         oos.close();
10         FileInputStream fis = new FileInputStream("a.txt");
11         ObjectInputStream ois = new ObjectInputStream(fis);
12         s1 = (LazySingleton) ois.readObject();
13     }
14 }

通过程序,s1与s的地址信息不同,证明为非单例对象

解决办法:

1 private Object readResolve(){
2    System.out.println("read resolve");
3    return instance;
4 }

加上此方法后,jdk中ObjectInputStream的类中有readUnshared()方法。

如果有该方法, 则会执行浅拷贝。【原因:去翻源码,还没看】

深拷贝和浅拷贝的区别,简述:

在JVM中,对象都保存在堆中,对象的引用都保存在栈中。

浅拷贝:只是赋值一个对象的引用,并指向在堆中的原对象。

深拷贝:栈中的引用和堆中的内存都赋值一份,并且新引用指向新内存。

所以,浅拷贝完全符合单例。

 

深拷贝,浅拷贝,以及加上readResolve方法后,为什么执行浅拷贝,以后补一下

 

以上是关于设计模式之单例模式的主要内容,如果未能解决你的问题,请参考以下文章

设计模式之单例模式

Java设计模式之单例模式

设计模式之单例模式以及简单代码实现

设计模式之单例设计模式

设计模式之单例模式

设计模式之单例模式