设计模式学习笔记------单例模式

Posted 有悟还有迷

tags:

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

             单例模式 

一、代码实例

1、恶汉单例模式

 1 /**
 2  * 恶汉单例模式
 3  * @author abc
 4  *
 5  */
 6 public class Singleton {
 7     /**
 8      * 定义一个静态变量存储创建好的实例
 9      */
10     private static Singleton singleton = new Singleton();
11     
12     /**
13      * 私有化构造函数,不让外部构造对象实例
14      */
15     private Singleton(){}
16     
17     /**
18      * 提供外部获取示例的方法
19      * @return
20      */
21     public static Singleton getInstance() {
22         return singleton;
23     }
24 }

2、懒汉单例模式

 1 /**
 2  * 懒汉单例模式
 3  * @author abc
 4  *
 5  */
 6 public class Singleton {
 7     /**
 8      * 定义一个静态变量用来存储单例对象
 9      */
10     private static Singleton singleton = null;
11     
12     /**
13      * 私有化构造函数,不让外部构造对象实例
14      */
15     private Singleton(){}
16     
17     /**
18      * 提供外部获取示例的方法
19      * @return
20      */
21     public static Singleton getInstance() {
22         if (singleton == null) {//判断静态变量是否有值
23             singleton = new Singleton();//没有值,则创建实例
24         }
25         return singleton;//有值,则直接返回
26     }
27 }

 

二、两种单例模式特点

  1.恶汉单例模式

    空间换时间:  不管用户使不使用,都先创建对象实例。---节约时间,浪费空间,有可能用户永远都不会使用。创建对象是比较着急,饿了嘛很着急,因此得名恶汉单例模式。

  2.懒汉单例模式

    时间换空间:  到用户需要时,在创建对象的示例。---节约空间,浪费时间,每次调用 getInstance() 时都需要判断,浪费时间,但是可以实现一种懒加载,只在需要使用时,才创建爱。因此得名为懒汉单例模式。

三、线程安全问题

  懒汉单例模式存在线程安全问题,当没有创建Singleton实例时,有多个线程同事调用 getInstance() 这是 singleton都为空,会导致Singleton创建多次。

  解决线程安全

 1 /**
 2  * 懒汉单例模式-线程安全
 3  * @author abc
 4  *
 5  */
 6 public class Singleton {
 7     
 8     /**
 9      * 对保存实例的变量添加volatile的修饰
10      */
11     private volatile static Singleton instance = null;
12     
13     /**
14      * 私有化构造方法
15      */
16     private Singleton() {
17         
18     }
19     
20     public static Singleton getInstance() {
21         //先检查实例是否存在,如果不存在才进入下面的同步快
22         if (instance == null) {
23             //同步快,线程安全的创建实例
24             synchronized(Singleton.class) {
25                 //再次检查实例是否存在,如果不存在才真正的创建实例
26                 if (instance == null) {
27                     instance = new Singleton();
28                 }
29             }
30         }
31         
32         return instance;
33     }
34 }

 先判断实例是否存在,再使用同步代码块,可以节约时间,只用在第一次调用实例是,进行同步操作,在创建完实例后,就只需判断实例是否创建,无需考虑同步。从而加快了运行的速度。

四、在java中一种更好的单例实现方式

  常见的两种单例模式实现方都存在小小的缺陷,那么有没有一种方案,既能实现延迟加载,又能够实现线程安全呢?

  Lazy initialization holder class模式,这个模式综合使用了Java的类级内部类和多线程缺省同步锁的知识,很巧妙的同事实现了延迟加载和线程安全。

  1.相应的基础知识

    先简单地看看类级内部类相关的知识

      a.什么是类级内部来?

      简单点说,类级内部类指的是,有static修饰的成员内部类。如果没有static修饰的成员内部类被称为对象级内部类。

      b.类级内部类相当于其外部类的static成分,他的对象,与外部类对象间不存在依赖关系,因此可以直接创建。而对象级内部类的实例,是绑定在外部对象实例中的。

      c.类级内部类中,可以定义静态的方法。在静态方法中只能够引用外部类中的静态成员方法或者成员变量。

      d.类级内部类相当于其外部类的成员,只有在第一次被使用时才会被装载。

    再来看看多线程缺省同步锁的知识

    大家都知道,在多线程开发中,为了解决并发问题,主要是通过使用 synchronized来加互斥锁进行同步控制。但是在某些情况中,JVM已经隐含地为您执行了同步,这些情况下就不用自己来进行同步控制了。这些情况包括:

      a.有静态初始化器(在静态字段上或 static{} 快中的初始化器)初始化数据时

      b.访问final字段时

      c.在创建线程之前创建对象是

      d.线程可以看见它将要处理的对象时

  2.解决方案的思路

    想要很简单的实现线程安全,可以采用静态初始化器的方式,它可以由JVM来保证线程的安全性。比如前面的恶汉式实现方式。但是这样一来,不是会浪费一定的空间吗?

  因为这种实现方式,会在类装载的时候就初始化对象,不管你需不需要。

    如果现在有一种方法能够让类装载的时候就初始化对象,那不就解决问题了?一种可行的方式就是采用内级内部类,在这个类级内部类里面去创建对象实例。这样一来,只要不使用到这个类级内部类,那就不会创建对象的实例,从而同事实现延迟加载和线程安全。

  代码示例:

 1 public class Singleton {
 2     
 3     /**
 4      * 类级内部类,就业是静态的成员式内部类,该内部类的实例与外部类的实例没有绑定关系
 5      * 而且只有被调用到时才会装载,从而实现了延迟加载
 6      * @author abc
 7      *
 8      */
 9     private static class SingletonHolder {
10         /**
11          * 静态初始化器,由JVM来保证线程安全
12          */
13         private static Singleton instance = new Singleton();
14     }
15     
16     /**
17      * 私有化构造方法
18      */
19     private Singleton() {}
20     
21     /**
22      * 获取方法
23      * @return
24      */
25     private static Singleton getInstance() {
26         return SingletonHolder.instance;
27     }
28     
29 }

  当 getInstance 方法第一次被调用的时候,他第一次读取 SingletonHolder.instance, 大致SingletonHolder 类得到初始化,而这个类在装载并被初始化的时候,会初始化的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证他的线程安全性。

  这个模式的优势在于,getInstance方法并没有被同步,并且只是执行一个域的访问,因此延迟初始化并没有增加任何访问成本。 

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

单例模式学习笔记

设计模式学习笔记--单例模式

设计模式学习笔记-单例模式

单例设计模式详解+源代码+JDK源码应用——Java设计模式系列学习笔记

单例模式的学习笔记

设计模式:学习笔记——单例模式