现在自己由于实现一些管理器(比如 mysq了连接的管理器,redis的连接管理器,以及config的连接管理器),经常用到单例模式,不过,公司内的单例模式的实现方法有很多,自己也跟着总结一下。


首先,为什么需要单例模式呢?比如,你需要获得一个mysql连接,你肯定希望所有mysql的连接池是被一个对象管理的,而不希望你从这个对象可以获得连接,从另一个对象也可以获得连接,所以,就很有必要让这个对象唯一。这时候就需要实现单例模式了。


单例的实现,根本上来说,是要让构造函数私有化,因为一旦构造函数暴露,总是可以通过new 来初始化这个对象的。


构造函数私有了,也就是只有本类之内能调用这些方法,所以下面介绍几种实现。


1.最简单的单例

public class Singleton {
    private final static Singleton instance = new Singleton(); 
    private Singleton(){}
    // some other code;
    
}

这个简单粗暴,直接设置一个静态变量,由于静态变量是类共有的,所有只会被调用一次。

简单粗暴,但是会有一些问题。

第一,这个类在加载的时候就必须创建这个对象,假设创建这个类的代价很高,那在服务启动时候,有很多这样的类的话,就导致速度很慢。

第二,假设这个类的构造函数调用了其他类的静态资源,这些静态变量这时候并没有被加载进来,这就会出现问题。


如果创建对象没什么代价,而且也不会用到其他变量的时候,使用这个方法还是很简单的。


2.延迟创建

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

这个方法,乍一看不错啊,只有当使用的时候才会创建这个对象。而且,这个方法确实不错啊,但是并发情况下这是很渣的。


3.于是看到一种比较狠的,使用了syschonised

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

                }
            }
        }
        return instance;
    }

}
<span style="font-family: Arial, Helvetica, sans-serif; background-color: rgb(255, 255, 255);">在这个方法中调用了同步锁,所以不会有并发问题。</span>

4.使用holder的方法,将管理实例完全封装起来

也就是实现一个内部类

public class Singleton {

    private Singleton(){}

    private static class SingletonHolder{
        private static Singleton instance = new Singleton();
    }
}

这个内部类对于外界来说完全透明,而且这个私有变量对外部的类来说是可以访问的,由于这个内部类只在使用的时候加载并且创建该实例,也是延迟创建的。

5.使用枚举,枚举天生就是单例,所以很好用的。


6.使用spring托管,这个不用说了。