单例模式

1. 定义

确保某个类只有一个实例,能自行实例化并向整个系统提供这个实例。

2. 应用场景

* 资源共享的情况下,避免由于资源操作时导致的性能或损耗等。如上述中的日志文件,应用配置。 
* 控制资源的情况下,方便资源之间的互相通信。如线程池等。 

3. 实现

3.1 饿汉单例模式

/**
* 饿汉单例模式
**/
public class EhanSingleton {
    //static final单例对象,类加载的时候就初始化
    private static final EhanSingleton instance = new EhanSingleton();

    //私有构造方法,使得外界不能直接new
    private EhanSingleton(){}

    //公有静态方法,对外提供获取单例接口
    public static EhanSingleton getInstance(){
        return instance;
    }
}
  • 优点:线程安全。因为在加载这个类的时候,就实例化了instance。当getInstatnce方法被调用时,得到的永远是类加载时初始化的对象(反序列化的情况除外)。
  • 缺点:如果有大量的类都采用了饿汉单例模式,那么在类加载的阶段,会初始化很多暂时还没有用到的对象,这样肯定会浪费内存,影响性能

3.2 懒汉单例模式

/**
 * 懒汉单例模式
 */
public class LanhanSingleton {
    //static单例对象,类加载的时候为null
    private static LanhanSingleton instance;

    //私有构造方法,使得外界不能直接new
    private LanhanSingleton(){}

    //增加synchronized关键字,该方法为同步方法,保证多线程单例对象唯一
    public static synchronized LanhanSingleton getInstance(){
        if(instance == null){
            instance = new LanhanSingleton();
        }
        return instance;
    }
}
  • 优点:单例对象在第一次调用才被实例化,有效节省内存,并保证了线程安全。
  • 缺点:同步是针对方法的,以后每次调用getInstance时(就算intance已经被实例化了),也会进行同步,造成了不必要的同步开销。不推荐使用这种方式。

3.3 Double CheckLock(DCL)单例模式

/**
 * 双重检测单例
 */
public class DCLSingleton {
    //被volatile修饰的变量的值,将不会被本地线程缓存
    //所有对该变量的读写都是直接操作共享内存,从而确保多个线程能正确的处理该变量。
    private volatile static DCLSingleton instance;

    private DCLSingleton(){}

    public static DCLSingleton getInstance() {
        if(instance == null){
            synchronized (DCLSingleton.class){
                //当第一次调用getInstance方法时,即instance为空时,同步操作,保证多线程实例唯一
                //当以后调用getInstance方法时,即instance不为空时,不进入同步代码块,减少了不必要的同步开销
                if(instance == null){
                    instance = new DCLSingleton();
                }
            }
        }
        return instance;
    }
}
  • 优点:
    • 第一次执行getInstance时instance才被实例化,节省内存;
    • 多线程情况下,基本安全;
    • 并且在instance实例化以后,再次调用getInstance时,不会有同步消耗。
  • 缺点:
    • jdk1.5以下,有可能DCL失效;
    • Java内存模型影响导致失效;
    • jdk1.5以后,使用volatile关键字,虽然能解决DCL失效问题,但是会影响部分性能。

3.4 静态内部类单例模式

/**
 * 静态内部类单例
 */
public class StaticSingleton {
    //私有的构造方法,防止new
    private StaticSingleton(){}

    /**
     * 静态内部类
     * 第一次加载内部类的时候,实例化单例对象
     */
    private static class StaticSingletonHolder{
        private static final StaticSingleton instance = new StaticSingleton();
    }

    public static StaticSingleton getInstance(){
        return StaticSingletonHolder.instance;
    }
}
  • 优点:第一次加载StaticClassSingleton类时,并不会实例化instance,只有第一次调用getInstance方法时,Java虚拟机才会去加载StaticClassSingletonHolder类,继而实例化instance。
  • 缺点:第一次加载时反应不够快

总结

名称 优点 缺点 备注
饿汉模式 线程安全 内存消耗太大
懒汉模式 线程安全 同步方法消耗比较大
DCL模式 线程安全,节省内存 jdk版本受限、高并发会导致DCL失效 推荐使用
静态内部类模式 线程安全、节省内存 实现比较麻烦 推荐使用

results matching ""

    No results matching ""