设计模式_单例模式

设计模式_单例模式


类的单例设计模式:采取一定的方法保证在整个系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法(静态)。

单例模式的特点

  1. 单例类只有一个实例对象;
  2. 该单例对象必须由单例类自行创建;
  3. 单例类对外提供一个访问该单例的全局访问点;

单例模式的结构

单例模式的主要角色如下。

  • 单例类:包含一个实例且能自行创建这个实例的类。
  • 访问类:使用单例的类。

图1

单例模式的实现

饿汉式
静态常量
class Singleton {
  private Singleton() {// 构造器私有化,防止外部new
  }
  // 本类内部创建新实例
  private final static Singleton instance = new Singleton();
  public static Singleton getInstance() {// 提供一个共有的静态方法,返回实例对象
    return instance;
  }
}

优点:写法简单,在类加载的时候即完成实例化,避免线程同步问题。

缺点:在类加载的时候完成实例化,没有达到Lazy loading的效果。如果该类的实例从未使用,可能会造成内存浪费。

这种基于classloader机制避免了多线程的同步问题。Instance在类加载的时候就实例化,在单例模式中大多都是调用getInstance方法,但是导致类加载的原因有很多,因此在其他原因导致类加载的时候,初始化instance就没有达到lazy loading的效果。

静态代码块
class Singleton {
  private Singleton() {// 构造器私有化,防止外部new
  }
  // 本类内部创建新实例
  private static Singleton instance;
  static {// 静态代码块中创建单例对象
    instance = new Singleton();
  }
  public static Singleton getInstance() {// 提供一个共有的静态方法,返回实例对象
    return instance;
  }
}

和静态常量的方式一样,同样是指类加载的时候创建实例,也可能造成内存浪费。

懒汉式
线程不安全
class Singleton {
  // 本类内部创建新实例
  private static Singleton instance;
  private Singleton() {// 构造器私有化,防止外部new
  }
  public static Singleton getInstance() {// 提供一个共有的静态方法,当使用到该方法到时候,采取新建实例对象
    if(instance == null) {
      instance = new Singleton();
    }
    return instance;
  }
}

​ 可以达到Lazy loading的效果,但是只能在单线程下使用。

多线程环境下,如果一个线程进入了if(instance == null)判断,还没有继续向下执行,另一个线程也完成了这个判定,这时就产生了多个实例。所有在多线程环境下不可使用。

由于存在创建多个实例对象的可能,破坏了单例模式,开发中不可使用。

线程安全(同步方法)
class Singleton {
  // 本类内部创建新实例
  private static Singleton instance;
  private Singleton() {// 构造器私有化,防止外部new
  }

  public static synchronized Singleton getInstance() {// 提供一个共有的静态方法,加入同步处理,解决线程安全问题
    if(instance == null) {
      instance = new Singleton();
    }
    return instance;
  }
}

​ 虽然通过同步方法的方式解决了线程安全问题,但是每一次getInstance()都需要进行同步,效率很低。

双重检查
class Singleton {
  // 本类内部创建新实例
  private static volatile Singleton instance;
  private Singleton() {// 构造器私有化,防止外部new
  }
  public static Singleton getInstance() {// 提供一个共有的静态方法,加入双重检测,同时解决线程安全、Lazy loading和效率问题。
    if (instance == null) {
      synchronized (Singleton.class) {
        if (instance == null) {
          instance = new Singleton();
        }
      }
    }
    return instance;
  }
}

​ 双重检查进行了两次if (instance == null)的检测,保证了线程安全。实例化代码只执行一次,后续访问,if (instance == null)判断之后会直接返回实例化对象,避免反复进行方法同步。推荐使用该方式。

变量声明为volatile类型后,编译器与运行时都会注意到这个变量是共享的,因此不会将该变量上的操作与其他内存操作一起重排序。volatile变量不会被缓存在寄存器或者对其他处理器不可见的地方,因此在读取volatile类型的变量时总会返回最新写入的值。

静态内部类
class Singleton {
  // 本类内部创建新实例
  private static volatile Singleton instance;
  private Singleton() {// 构造器私有化,防止外部new
  }
  private static class SingletonInstance {//静态内部类,该类中有一个静态属性Singleton
    private static final Singleton instance = new Singleton();
  }
  public static Singleton getInstance() {// 提供一个共有的静态方法,直接返回SingletonInstance.instance。
    return SingletonInstance.instance;
  }
}

​ 由于Jvm加载类的过程是线程安全的(整个过程都是同步的),而且在加载Singleton类的时候并不会同时加载其内部类SingletonInstance,只有在调用getInstance()方法的时候才会加载SingletonInstance类并切完成实例化。

​ 这样利用Jvm保证了线程安全,同时达到了Lazy loading的效果。推荐使用该方式。

枚举
enum Singleton {
  INSTANCE;//属性
  public void saySomething() {
    System.out.println("hello singleton.");
  }
}

​ 避免了多线程的同步问题,而且还能防止反序列化重新创建信的对象。

单例模式的应用场景

  • 当对象需要被共享的场合。由于单例模式只允许创建一个对象,共享该对象可以节省内存,并加快对象访问速度。如 Web 中的配置对象、数据库的连接池等。
  • 当某类需要频繁实例化,而创建的对象又频繁被销毁的时候,如多线程的线程池、网络连接池等。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值