单例模式的几种写法

单例模式的编写需要注重一下几点:

  • 是否懒加载
  • 是否线程安全
  • 是否通过反射、克隆、序列化进行破坏

第三点也是面试中长问的,其实让其无法调用2次构造函数即可:

private Singleton(){
        if(instance != null){
            throw new RuntimeException("单例模式禁止反射创建实例!");
        }
    }

这个只是其中一种方式,其它方式也是大同小异,不让调用2次构造函数。

下面从两个方面分别去讨论单例模式。

一、饿汉模式

饿汉模式,就是在类加载的时候,初始化单例对象,代码很简单:

/**
 * 饿汉模式 -  线程安全,但是不是懒加载
 */
public class Singleton1 {

    private static Singleton1 singleton = new Singleton1();

    private Singleton1(){}

    public static Singleton1 getInstance(){
        return singleton;
    }

}

优点是线程安全,缺点是在类初始化的时候需要加载对象。

二、懒汉模式

懒汉模式,根据名字就知道,不调用不初始化,调用才初始化

/**
 * 懒汉模式  - 懒加载,线程不安全
 */
public class Singleton2 {

    private static Singleton2 singleton2;

    private Singleton2(){}

    private static Singleton2 getInstance(){
        if(singleton2 == null){
            singleton2 = new Singleton2();
        }
        return singleton2;
    }

}

优点是不用的时候不会初始化实例,但是线程不安全的。

三、懒汉模式+同步锁

懒汉模式,增加同步锁,就可以避免线程安全

/**
 * 懒汉模式 - 增加同步锁,线程安全,但是效率很低
 */
public class Singleton3 {

    private static Singleton3 singleton3;

    private Singleton3(){}

    private static synchronized Singleton3 getInstance(){
        if(singleton3 == null){
            singleton3 = new Singleton3();
        }
        return singleton3;
    }

}

synchronized的通病,效率低下。

四、懒汉模式+DCL

双次验证 + 布局锁 + 防止重排

/**
 * DCL(Double Check Lock)- 双检锁
 * 1、静态代码块内部的判断,防止外面的等待的线程进入之后创建对象
 * 2、volatile 防止指令重排
 */
public class Singleton4 {

    private volatile static Singleton4 singleton4;

    private Singleton4(){}

    public static Singleton4 getInstance(){
        if(singleton4 == null){
            synchronized (Singleton4.class){
                if(singleton4 == null){
                    singleton4 = new Singleton4();
                }
            }
        }
        return singleton4;
    }

}

五、静态内部类

调用static属性,让静态内部类初始化

/**
 * 懒汉模式 - 静态内部类变种
 * 这种方式,也属于懒加载
 * 调用了Singleton5Holder 的静态变量,是主动调用,所以Singleton5Holder会被初始化,它的静态变量singleton5 会被赋值
 */
public class Singleton5 {

    private static class Singleton5Holder{
        private static final Singleton5 singleton5 = new Singleton5();
    }

    private Singleton5(){}

    public static Singleton5 getInstance(){
        return Singleton5Holder.singleton5;
    }
}

六、枚举类型

枚举类天然的就是单例模式,并且因为没有无参构造函数,所以可以防止反射等破坏行为。

你可能感兴趣的:(设计模式,单例模式)