设计模式之单例模式

一个类 Class 只有一个实例存在,单机服务器保证线程安全。为了节约系统资源,无须创建多个实例,有时需要确保系统中某个类只有唯一一个实例。

Java 实现例子

方式一,饿汉模式

private static SingletonModelA instance = new SingletonModelA();

public static SingletonModelA getInstance() {
    return instance;
}

方式二,懒汉模式

private static SingletonModelB instance = null;

// 方式 A
public static synchronized SingletonModelB getInstance1() {
    if (instance == null) {
        instance = new SingletonModelB();
    }
    return instance;
}

// 方式 B
public static SingletonModelB getInstance2() {
    synchronized (SingletonModelB.class) {
        if (instance == null) {
            instance = new SingletonModelB();
        }
    }
    return instance;
}

方式三,双层判断

参考 为什么要加 volatile?

private static volatile SingletonModelC instance = null;

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

方式四,使用静态内部类

推荐使用,内部类只有在 getInstance() 方法第一次调用的时候才会被加载(实现了延迟加载效果),而且其加载过程是线程安全的(实现线程安全)。

private static class SingletonHandler {
    private static SingletonModelD instance = new SingletonModelD();
}

public static SingletonModelD getInstance() {
    return SingletonHandler.instance;
}

单例的破坏

单例的实现,把构造方法设置为私有方法来避免外部调用是很重要的一个前提。但是,可以通过反射来调用类中的私有方法的,构造方法也不例外,所以,可以通过反射来破坏单例。

除了这种情况,还有一种比较容易被忽视的情况,那就是对象的序列化和反序列化也会破坏单例。

如使用 ObjectInputStream 进行反序列化时,在 ObjectInputStream 的 readObject 生成对象的过程中,其实会通过反射的方式调用无参构造方法新建一个对象。

在单例类中定义 readResolve 的方式,解决该问题:

public class Singleton implements Serializable{
    private volatile static Singleton singleton;
    private Singleton (){}
    public static Singleton getSingleton() {
        if (singleton == null) {
            synchronized (Singleton.class) {
                if (singleton == null) {
                    singleton = new Singleton();
                }
            }
        }
        return singleton;
    }

    private Object readResolve() {
        return singleton;
    }
}

如果不涉及序列化的话,可以不用写此方法


方式五,枚举定义

在《Effective Java》中明确表达过的观点:使用枚举实现单例的方法虽然还没有广泛采用,但是单元素的枚举类型已经成为实现单例的最佳方法。

public enum Singleton {
    INSTANCE;

    public void whateverMethod() {
    }
}

以上,就实现了一个非常简单的单例,从代码行数上看,他比之前介绍过的任何一种都要精简,并且,他还是线程安全的。

同时,枚举可解决反序列化破坏单例的问题。详细查看 Java 枚举类型


Add a Comment

电子邮件地址不会被公开。 必填项已用*标注