单例模式

简介

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

特点

  • 单例模式保证整个应用中某个实例有且只有一个。
  • 有些对象我们只需要一个,比如:
    • 在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。
    • 每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。
  • 如果创造出多个实例,就会导致许多问题,比如占用过多资源,不一致的结果等

总之,选择单例模式就是为了避免不一致状态,避免政出多头。

实现

一.饿汉模式--急着加载

单例会在加载类后一开始就被初始化,即使客户端没有调用 getInstance()方法。

  • 优点
    在类创建的同时就已经创建好一个静态的对象供系统使用,以后不再改变,所以天生是线程安全的
  • 缺点
    所以饿汉式的创建方式在一些场景中将无法使用:譬如 Singleton 实例的创建是依赖参数或者配置文件的,在 getInstance() 之前必须调用某个方法设置参数给它
public class Singleton {
    //1.构造方法私有化,不允许外部直接创建对象
    private Singleton(){
    }

    //2.创建类的唯一实例,使用private static修饰
    private static Singleton instance=new Singleton();

    //3.提供一个用于获取实例的方法,使用public static修饰
    public static Singleton getInstance(){
        return instance;
    }
}

二.懒汉式--等着加载

这段代码简单明了,但是当有多个线程并行调用 getInstance() 的时候,就会创建多个实例。也就是说在多线程下不能正常工作。

public class Singleton {
    private static Singleton instance;
    private Singleton (){
    }

    public static Singleton getInstance() {
       if (instance == null) {
           instance = new Singleton();
       }
     return instance;
    }
}

并发环境下很可能出现多个Singleton实例,要实现线程安全,有以下三种方式,都是对getInstance这个方法改造,保证了懒汉式单例的线程安全

2.1整个 getInstance() 方法设为同步

最简单的方法是将整个 getInstance() 方法设为同步(synchronized)。

public static synchronized Singleton getInstance() {
    if (instance == null) {
        instance = new Singleton();
    }
    return instance;
}

虽然线程安全了,但是每次都要同步,会影响性能,毕竟大部分情况下是不需要同步的

2.2双重检查锁定

public static Singleton getInstance() {  
    if (singleton == null) {    
        synchronized (Singleton.class) {    
            if (singleton == null) {    
                singleton = new Singleton();   
            }    
        }    
    }    
    return singleton;   
}  

第一个if (instance == null),其实是为了解决Version2中的效率问题,只有instance为null的时候,才进入synchronized的代码段——大大减少了几率。
第二个if (instance == null),则是跟Version2一样,是为了防止可能出现多个实例的情况。

2.3静态内部类

这种写法使用JVM本身机制保证了线程安全问题;由于 SingletonHolder 是私有的,除了 getInstance() 之外没有办法访问它,因此它是懒汉式的;同时读取实例的时候不会进行同步,没有性能缺陷。

public class Singleton {  
    private static class SingletonHolder {  
        private static final Singleton INSTANCE = new Singleton();  
    }  
    private Singleton (){
    }  

    public static final Singleton getInstance() {  
        return SingletonHolder.INSTANCE; 
    }  
}

内部类

三.枚举

public enum Singleton{
    INSTANCE;
}

我们可以通过 Singleton.INSTANCE 来访问实例,这比调用getInstance()方法简单多了。

  • 创建枚举默认就是线程安全的,所以不需要担心double checked locking
  • 而且还能防止反序列化导致重新创建新的对象

饿汉模式和懒汉模式的对比:

  • 初始化时机
    饿汉就是类一旦加载,就把单例初始化完成,保证getInstance的时候,单例是已经存在的了,
    而懒汉比较懒,只有当调用getInstance的时候,才回去初始化这个单例。
  • 特点
    饿汉模式加载类时比较慢,但运行时获取对象的速度比较快,线程安全;
    懒汉模式加载类时比较快,但运行时获取对象的速度比较慢,线程不安全。

选择

一般情况下直接使用饿汉式就好了,如果明确要求要懒加载(lazy initialization)可以使用静态内部类,如果涉及到反序列化创建对象时会试着使用枚举的方式来实现单例。

你可能感兴趣的:(单例模式)