单例模式

单例模式

概念

单例模式(SingletonPattern)是指确保一个类在任何情况下都绝对只有一个实例,并提供一个全局访问点。单例模式属于创建型模式。

饿汉式单例模式

典型饿汉式单例模式
public class HungrySingleton {

    private static final HungrySingleton instance = new HungrySingleton();

    private HungrySingleton(){}

    public static HungrySingleton getInstance(){
        return instance;
    }
}
饿汉式静态块单例模式
public class HungryStaticSingleton {

    private static final HungryStaticSingleton instance;

    static {
        instance = new HungryStaticSingleton();
    }

    private HungryStaticSingleton(){}

    public static HungryStaticSingleton getInstance(){
        return instance;
    }
}
优点

没有加任何锁、执行效率比较高

缺点

类加载的时候就初始化,不管用与不用都占着空间,浪费内存

懒汉式单例模式

简单懒汉式单例实现
public class LazySimpleSingleton {

    private static LazySimpleSingleton instance;

    private LazySimpleSingleton(){}

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

在多线程环境下,这种写法存在安全隐患,可能创建出两个不同的实例,违背了单例原则。优化如下:

/**
 * 优点:节省了内存,线程安全
 * 缺点:性能低
 */
public class LazySimpleSingleton {

    private static LazySimpleSingleton instance;

    private LazySimpleSingleton(){}

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

通过 synchronized 关键字加锁可以解决线程安全问题,但会降低性能。

双重检查锁单例(DCL单例)

/**
 * 优点:性能高了,线程安全了
 * 缺点:可读性难度加大,不够优雅
 */
public class DCLSingleton {

    private volatile static DCLSingleton instance;

    private DCLSingleton(){}

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

静态内部类单例

//这种形式兼顾饿汉式单例模式的内存浪费问题和 synchronized 的性能问题
//完美地屏蔽了这两个缺点
public class InnerClassSingleton {

    private InnerClassSingleton(){}

    public static final InnerClassSingleton getInstance(){
        return LazyHolder.INNER_CLASS_SINGLETON;
    }

    private static class LazyHolder{
        private static final InnerClassSingleton INNER_CLASS_SINGLETON = new InnerClassSingleton();
    }
}

反射破坏单例

以 InnerClassSingleton 为例:

public class InnerClassSingletonTest {
    public static void main(String[] args) {
        try {
            Class clazz = InnerClassSingleton.class;
            Constructor constructor = clazz.getDeclaredConstructor();
            constructor.setAccessible(true);
            InnerClassSingleton instance = (InnerClassSingleton) constructor.newInstance();
            InnerClassSingleton instance2 = (InnerClassSingleton) constructor.newInstance();
            System.out.println(instance == instance2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }
}

运行结果为 false ,单例被破坏。

对 InnerClassSingleton 进行优化,防止反射破坏单例。

public class InnerClassSingleton {

    private InnerClassSingleton(){
        if (LazyHolder.INNER_CLASS_SINGLETON != null) {
            throw new RuntimeException("不允许创建多个实例");
        }
    }

    public static final InnerClassSingleton getInstance(){
        return LazyHolder.INNER_CLASS_SINGLETON;
    }

    private static class LazyHolder{
        private static final InnerClassSingleton INNER_CLASS_SINGLETON = new InnerClassSingleton();
    }
}

注册式单例之枚举式单例

枚举式单例典型写法如下:

public enum EnumSingleton {
    INSTANCE;

    private Object data;

    public Object getData() {
        return data;
    }

    public void setData(Object data) {
        this.data = data;
    }

    public static EnumSingleton getInstance() {
        return  INSTANCE;
    }
}

测试:

public static void main(String[] args) {
//        EnumSingleton instance = EnumSingleton.getInstance();
        try {
            Class clazz = EnumSingleton.class;
            Constructor constructor = clazz.getDeclaredConstructor(String.class, int.class);
            //System.out.println(constructor);
            constructor.setAccessible(true);
            Object instance = constructor.newInstance();
            System.out.println(instance);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

运行结果:

java.lang.IllegalArgumentException: Cannot reflectively create enum objects
    at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
    at com.aaron.pattern.singleton.test.EnumSingletonTest.main(EnumSingletonTest.java:15)

可以知道,枚举类型无法通过反射创建实例。

注册式单例之容器式单例

public class ContainerSingleton {
    private ContainerSingleton() {}

    private static Map ioc = new ConcurrentHashMap<>();

    public static Object getInstance(String className){
        Object obj = null;
        synchronized (ioc) {
            if (!ioc.containsKey(className)) {
                try {
                    obj = Class.forName(className).newInstance();
                    ioc.putIfAbsent(className, obj);
                } catch (Exception e) {
                    e.printStackTrace();
                }
                return obj;
            } else {
                return ioc.get(className);
            }
        }
    }
}

序列化破坏单例

一个单例对象创建好后,有时候需要将对象序列化然后写入磁盘,下次使用时再从磁盘中读取对象
并进行反序列化,将其转化为内存对象。反序列化后的对象会重新分配内存,即重新创建。如果序列化的目标对象为单例对象,就违背了单例模式的初衷,相当于破坏了单例,来看一段代码:

//反序列化导致破坏单例模式
public class SeriableSingleton implements Serializable {
    //序列化就是把内存中的状态通过转换成字节码的形式
    //从而转换一个 I/O 流,写入其他地方(可以是磁盘、网络 I/O)
    //内存中的状态会永久保存下来
    //反序列化就是将已经持久化的字节码内容转换为 I/O 流
    //通过 I/O 流的读取,进而将读取的内容转换为 Java 对象
    //在转换过程中会重新创建对象 new
    public final static SeriableSingleton INSTANCE = new SeriableSingleton();
    private SeriableSingleton(){}
    public static SeriableSingleton getInstance(){
        return INSTANCE;
    }
}

测试代码如下

public static void main(String[] args) {
        SeriableSingleton s1 = null;
        SeriableSingleton s2 = SeriableSingleton.getInstance();

        FileOutputStream fos = null;
        try {
            fos = new FileOutputStream("SeriableSingleton.obj");
            ObjectOutputStream oos = new ObjectOutputStream(fos);
            oos.writeObject(s2);
            oos.flush();
            oos.close();

            FileInputStream fis = new FileInputStream("SeriableSingleton.obj");
            ObjectInputStream ois = new ObjectInputStream(fis);
            s1 = (SeriableSingleton) ois.readObject();
            ois.close();

            System.out.println(s1);
            System.out.println(s2);
            System.out.println(s1 == s2);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

测试代码运行结果如下:

com.aaron.pattern.singleton.seriable.SeriableSingleton@6d03e736
com.aaron.pattern.singleton.seriable.SeriableSingleton@14ae5a5
false

可以通过增加 readResolve() 方法保证单例,代码优化:

public class SeriableSingleton implements Serializable {

    //序列化就是把内存中的状态通过转换成字节码的形式
    //从而转换一个 I/O 流,写入其他地方(可以是磁盘、网络 I/O)
    //内存中的状态会永久保存下来

    //反序列化就是将已经持久化的字节码内容转换为 I/O 流
    //通过 I/O 流的读取,进而将读取的内容转换为 Java 对象
    //在转换过程中会重新创建对象 new
    private static final SeriableSingleton instance = new SeriableSingleton();

    private SeriableSingleton(){}

    public static SeriableSingleton getInstance(){
        return instance;
    }

    private Object readResolve() {
        return instance;
    }
}

再次运行测试:

com.aaron.pattern.singleton.seriable.SeriableSingleton@14ae5a5
com.aaron.pattern.singleton.seriable.SeriableSingleton@14ae5a5
true

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