Java 单例模式 Singleton Pattern

饿汉式 线程安全

public class Singleton {
    private final static Singleton instance = new Singleton();

    private Singleton() {
        System.out.println("Constructing Singleton");
    }

    public static Singleton getInstance() {
        return instance;
    }
}
  • instance 声明为 私有静态 变量, 在类加载时即调用构造函数,因此线程安全
  • 缺点:不管是否调用 getInstance 使用实例,都会占用内存空间

Double Checked Locking 线程安全

public class Singleton {
    private static Singleton instance;

    private Singleton() {
        System.out.println("Constructing Singleton");
    }

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

        return instance;
    }
}

注意事项:

  • instance 声明为 私有静态 变量
  • 构造函数声明为 私有
  • getInstace 函数声明为 公有静态 方法
  • 可能存在指令重排的问题。
    • 解决:用 volatile 修饰 instance private static volatile Singleton instance;
    • volatile修饰的话就可以确保 instance = new Singleton(); 对应的指令不会重排序

占位符模式 线程安全

public class Singleton {
    // 成员内部类
    private static class SingletonHolder {
        static Singleton instance = new Singleton();
    }

    private Singleton() {
        System.out.println("Constructing Singleton");
    }

    public static Singleton getInstance() {
        return SingletonHolder.instance;
    }
}
  • 内部类 SingletonHolder 只有在 getInstance() 方法第一次调用的时候才会被加载(实现了lazy,避免了饿汉模式的缺点)
  • static 域中修改共享变量是线程安全的,由JVM保障

通过反射机制攻击单例模式

public static void main(String[] args) throws Exception {
    Constructor c = Singleton.class.getDeclaredConstructor(null);
    c.setAccessible(true);

    Singleton s1 = (Singleton)c.newInstance();
    Singleton s2 = (Singleton)c.newInstance();
}

以上的代码可以多次调用构造函数,生成多个对象实例。

可以通过修改构造函数抵御这种攻击:

private static boolean flag = true;
// 私有构造函数
private Singleton() {
    synchronized (Singleton.class) {
        if(flag) {
            flag = !flag;
            System.out.println("Constructing Singleton");
        } else {
            throw new RuntimeException("Reject Constructing Singleton");
        }
    }
}

引用:
如何防止单例模式被JAVA反射攻击

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