设计模式23式——第1式单例模式

84 阅读3分钟

单例模式(Singleton Pattern)

定义:

Ensure a class has only one instance, and provide a global point of access to it.(确保某一个类只有一个实例,而且自行实例化并向整个系统提供这个实例。)

类图

image.png

代码

/**
* 饿汉模式:
* 线程安全,但是对象不是懒加载
*/
public class Singleton{
    private static final Singleton instance = new Singleton();
    // 私有化构造方法
    private Singleton(){}
    
    public static Singleton getInstance(){
        return instance;
    }
}

/**
* 懒汉模式:
* 获取实例的方法添加的synchronized关键字,进行操作加锁,这就导致性能下降。
*/
public class Singleton{
    private static final Singleton instance;
    // 私有化构造方法
    private Singleton(){}
    
    public static synchronized Singleton getInstance(){
        if (instance == null){
            instance = new Singleton();
        }
        return instance;
    }
}

/**
* 懒汉模式:Double check(双重检查)
* 
*/
public class Singleton{
    private static final Singleton instance;
    // 私有化构造方法
    private Singleton(){}
    
    public static Singleton getInstance(){
        if (instance == null){
            synchronized(Singleton.class){
                if (instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}


/**
* 利用静态内部类的方式实现单例类,利用了Java 静态内部类的特性:
* Java 加载外部类的时候,不会创建内部类的实例,只有在外部类使用到内部类的时候才会创建内部类实例。
*/
public class Singleton{
    // 私有化构造方法
    private Singleton(){}
    
    private static class SingletonInner { 
        private static final Singleton instance = new Singleton();
    }
    
    public static Singleton getInstance(){
        return SingletonInner.instance;
    }
}

/**
* 利用枚举实现
*/
public class Singleton{
    // 私有化构造方法
    private Singleton(){}
    
    private static enum SingletonEnum {
        INSTANCE;

        private Singleton instance = null;
        private SingletonEnum(){
            instance = new Singleton();
        }

    }

    public static Singleton getInstance(){
        return SingletonEnum.INSTANCE.instance;
    }
}

为什么要用单例模式?

先说说单例模式的优点:

  1. 由于单例模式在内存中只有一个实例,减少了内存开支;
  2. 只生成一个实例,减少了性能开销;
  3. 可以避免对资源的多重占用;
  4. 单例模式可以在系统设置全局的访问点,优化和共享资源访问。

说完优点,我们也说说缺点:

  1. 单例模式一般没有接口,所以扩展困难;
  2. 单例模式与单子职责原则有冲突。一个类应该只实现一个逻辑,而不关心是否是单例的,是不是要单例取决于环境。

使用场景

  1. 要求生成唯一序号的环境;
  2. 在整个项目中需要一个共享访问点或共享数据,例如:统计在线人数等;
  3. 创建一个对象需要消耗的资源过多的场景,如访问io和数据库等资源;
  4. 需要定义大量静态常量和静态方法(如工具类)的环境,可以采用单例模式(也可以直接声明为static)。

结束语

单例模式是23个设计模式中比较简单的,应用也比较广泛,比如Spring中,每个Bean默认就是单例,这样做的优点是Spring容器可以管理这些Bean的生命周期(创建、初始化、使用、销毁)。

使用单例模式的时候,我们要注意JVM的垃圾回收机制,如果我们创建的单例对象长时间不使用,被JVM当做垃圾清理掉,下次调用就需要重新创建一个对象,此时这个类的之前记录的状态值就会出现恢复原状的情况。

针对这种情况解决方式:

  1. 我们可以由容器管理单例对象的生命周期;
  2. 状态随时记录,采用异步记录或者使用观察者模式,记录状态的变化,写入文件或写入数据库中,避免应用数据丢失。