【干货】无可挑剔的几种Singleton写法

饿汉式

public class Singleton1 {
    /**
     * 声明final static,保证全局可访问且不可变。<br>
     * 声明transient,保证不会被序列化,当然也就不会反序列化生成对象了。
     */
    private final static transient Singleton1 instance = new Singleton1();

    private Singleton1() {
        /**
         * 防止通过反射生成对象
         */
        if (instance != null) {
            throw new RuntimeException("");
        }
    }

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

不管你用不用,只要类初始化了,对象就在那里。

懒汉式

public class Singleton2 {
    /**
     * volatile:保证对象生成后,其它线程立即可见,避免多余的加锁流程。<br>
     * transient:保证不会被序列化,当然也就不会反序列化生成对象了。
     */
    private static volatile transient Singleton2 instance = null;

    private Singleton2() {
        /**
         * 防止通过反射生成对象
         */
        if (instance != null) {
            throw new RuntimeException("");
        }
    }

    public static Singleton2 getInstance() {
        //用到了双重检测和加锁,目的是最大化的提高程序的执行速度
        if (instance == null) {// 检测对象是否已经生成,此时不加锁。
            synchronized (Singleton2.class) {//加锁
                if (instance == null)// 加锁情况下检测对象是否已经生成,防止线程在等待锁时其它线程已经生成了对象
                    instance = new Singleton2();
            }
        }
        return instance;
    }
}

只有在使用时才会生成对象,但会导致线程加锁,线程竞争严重时不建议使用该方式。

静态内部类

public class Singleton3 {
    private static class SingletonHolder {
        private final transient static Singleton3 instance = new Singleton3();
    }

    private Singleton3() {
        /**
         * 防止通过反射生成对象
         */
        if (SingletonHolder.instance != null) {
            throw new RuntimeException("");
        }
    }

    public static Singleton3 getInstance() {
        return SingletonHolder.instance;
    }
}

同时具有懒汉和饿汉两个模式的优点:避免多线程竞争,而且只在使用时才实例化对象。

枚举式

public enum Singleton4 {
    INSTANCE;
    public void doSomething() {
        System.out.println("aha");
    }
}

public static void main(String[] args) {
        Singleton4.INSTANCE.doSomething();
}

代码安全无竞争,写法简洁到想哭。

反思

我觉得没有必要关注太细的细节,比如防止反射,防止序列化;大部分情况下,我们在开发代码时都已经预知了单例的使用场景。
大神闫宏在设计模式一书中提到一种单例模式:构造函数是public的。这样我们可以在使用单例时调用getInstance方法,使用多例时直接new一个就好。因为这个类的使用场景是预先知道的。谁又能说这不是一个很好的单例模式?
不必在意茴香豆有几种写法,会写茴香豆几个字就好。

猜你喜欢

转载自blog.csdn.net/jeffrey_li/article/details/81540668