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

三、单例模式

​ 单例模式(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
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值