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

来源:互联网 发布:淘宝最迟发货时间 编辑:程序博客网 时间:2024/05/29 10:41

三、单例模式

​ 单例模式(Singleton Pattern)是Java中最简单的设计模式之一,涉及到一个单一的类,负责创建自己的对象且确保唯一,并提供对外调用方式。

  • 单例类只能有一个实例对象
  • 单例类必须自己创建自己的唯一实例对象
  • 单例类必须给所有其他对象提供这一实例。

介绍

​ 单例类提供一个可供全局访问的唯一的实例对象,用于全局频繁操作的单一对象,有助于节省系统资源。单例模式构造函数私有

应用场景举例:1、多线程文件操作时候,操作文件的对象需要单一,以保证操作安全(当然需要配合线程同步)。2、唯一序列号的产生。3、web网站全局计数器。

  • 优点:1、内存里只有一个实例,减少内存开销,尤其是频繁的创建与销毁实例。2、避免对资源的多重占用(如操作文件)。
  • 缺点:没有接口,不能继承,与单一职责原则冲突,一个类应该只关心内部逻辑,而不应关心外部实例化的问题。

**注意事项:**getInstance()方法中需要使用同步锁synchronized(Singleton.class)防止多线程同时进入造成instance多次实例化。

实现

创建一个SingleObject类,私有化其构造函数并创建一个静态实例,对外提供一个静态方法,来获取它的静态实例。SingletonPatternDemo,来演示获取SingleObject的单例对象。

singleton

  • 创建Singleton类

    SingleObject.java

public class SingleObject {  //创建一个SingleObject的对象  private static SingleObject instance = new SingleObject();  //私有化构造函数  private SingleObject(){}  //对外提供对象实例  public static SingleObject getInstance(){    return instance;  }  //单例类的其他方法  public void showMsg(){    System.out.println("Hello World!");  }}
  • 获单一实例

    SingletonPatternDemo.java

public class SinglePatternDemo {  public static void main(String[] args){    //单例模式对象实例的获取,不能用new的方式,因为其构造函数私有化了,只能通过它提供的实例获取方法获取实例对象。    SingleObject object = SingleObject.getInstance();    //调用对象的其他方法    object.showMsg();  }}
  • 输出结果
Hello World!

单例模式的集中实现方式

单例模式有多种实现模式

  1. 懒汉式、线程不安全

    lazy loading、非线程安全、简单易实现;由于非线程安全,严格意义上不算单例模式。但是在不要求线程安全的情况下,简便易用。

public class Singleton {  private static Singleton instance;  private Singleton(){}  //对外提供对象单例  public static Singleton getInstance(){    if (instance == null){      instance = new Singleton();    }    return instance;  }}

以下都是线程安全的单例模式,效率不同

  1. 懒汉式、线程安全

    线程安全形式的lazy loading,支持多线程,简单,但是效率低。

    • 优点:第一次调用才初始化,避免内存浪费。
    • 缺点:必须加synchronized锁才能保证单例,而影响效率。
public class Singleton {  private static Singleton instance;  private Singleton(){}  //同步锁方法,对外提供单例对象  public static synchronized Singleton getInstance(){    if (instance == null){      instance = new Singleton();    }    return instance;  }}
  1. 饿汉式

    饿汉式简单常用,线程安全,但是易产生垃圾。

    • 优点:无加锁,效率高。
    • 缺点:静态初始化,浪费内存。

    饿汉式通过classloader机制来避免多线程同步问题。但并没有达到lazy loading的记载效果。

public class Singleton {  private static Singleton instance = new Singleton();  private Singleton(){}  //类似懒汉式,但是静态初始化对象实例了。  public static Singleton getInstance(){    ruturn instance;  }}
  1. 双检锁/双重校验锁(DCL,即double-checked locking)

    采用双锁机制,多线程下高效执行,但是实现难度大。lazy loading、线程安全、复杂。且在JDK1.5后采用。

public class Singleton {  private volatile static Singleton singleton;  private Singleton(){}  //  public static Singleton getInstance(){    if (singleton == null){      //同步代码块      synchronized (Singleton.class){        //再次判断是否非空,避免多线程风险。        if (singleton == null){          singleton = new Singleton();        }      }    }    return singleton;  }}
  1. 登记式/静态内部类

    lazy loading、线程安全、难度一般。该形式可以达到如上双检锁一样的功效,实现更方便。仅适用于静态域延迟初始化,而双检锁方式可在实例域需要延迟初始化时使用。

    登记式同样利用了classloader机制,不同于饿汉式:1、饿汉式的instance随Singleton

    类初始化,没有lazy loading(lazy可避免浪费内存);而登记式的instance不一定随Singleton初始化,因为Singleton Holder类没有被主动使用。所以登记式比饿汉式更合理。

public class Singleton {  private static class SingletonHolder {    private static final Singleton INSTANCE = new Singleton();  }  private Singleton(){}  //登记式对外单例  public static final Singleton getInstance(){    return SingletonHolder.INSTANCE;  }}
  1. 枚举

    不lazy loading、线程安全、简单。这是实现单例模式最佳的方式,但开发中使用者较少。简洁、自动序列化、绝对防止多次实例化。JDK1.5以后采用。

public enum Singleton {  INSTANCE;  public void whateverMethod(){  }}

Note:一般不建议使用第1、2种懒汉方式,建议使用第3中饿汉式。只有明确实现lazy loading效果时才使用第5种登记式。涉及到反序列化对象时候,尝试第6种枚举式。若有特殊要求,可以考虑第4中双检锁方式。

0 0
原创粉丝点击