一、单例模式的优点和缺点

单例模式的优点:

  • 单例模式可以保证内存里只有一个实例,减少了内存的开销。
  • 可以避免对资源的多重占用。
  • 单例模式设置全局访问点,可以优化和共享资源的访问。

单例模式的缺点:

  • 单例模式一般没有接口,扩展困难。如果要扩展,则除了修改原来的代码,没有第二种途径,违背开闭原则。
  • 在并发测试中,单例模式不利于代码调试。在调试过程中,如果单例中的代码没有执行完,也不能模拟生成一个新的对象。
  • 单例模式的功能代码通常写在一个类中,如果功能设计不合理,则很容易违背单一职责原则。

二、单例模式的实现

1.懒汉式

优点:单例只有在使用时才被实例化,一定程度上节约了资源

缺点:非线程安全。

public class LazySingleton {

    private static volatile LazySingleton instance = null;

    private LazySingleton(){

    }

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

2.饿汉式

说明:声明静态时已经初始化,在获取对象之前就初始化

优点:获取对象的速度快,线程安全(因为虚拟机保证只会装载一次,在装载类的时候不会发生并发)

缺点:耗内存(若类中有静态方法,在调用静态方法的时候类就会被加载,类加载的时候就完成了单例的初始化,拖慢速度)

public class HungrySingleton {

    private static final HungrySingleton instance = new HungrySingleton();

    private HungrySingleton(){
    }

    public static HungrySingleton getInstance(){
        return instance;
    }
}

3.双重检查锁(推荐)

优点:既能保证线程安全,且单例对象初始化后调用getInstance不进行同步锁,资源利用率高

缺点:第一次加载稍慢,由于Java内存模型一些原因偶尔会失败,在高并发环境下也有一定的缺陷,但概率很小。

public class SingletonDoubleKey {

    private volatile static SingletonDoubleKey instance = null; //volatitle是为了避免双重锁定失效

    private SingletonDoubleKey(){}

    //synchronized 如果加在方法上,每次访问都进行同步锁,浪费性能。
    public static SingletonDoubleKey getInstance(){ //
        if (instance == null){  //避免不必要的同步
            synchronized (SingletonDoubleKey.class){  //类锁,synchronized(this)是对象锁。
                if(instance == null){  //在null的情况下创建实例
                    instance = new SingletonDoubleKey();
                }
            }
        }
        return instance;
    }

}