懒汉式-线程不安全

在以下代码中,Singleton的实例化被延迟执行,只在需要的时候进行初始化
但是也是线程不安全的,可能存在多个线程进入getUniqueInstance方法

  1. public class Singleton {
  2. private static Singleton uniqueInstance;
  3. private Singleton() {
  4. }
  5. public static Singleton getUniqueInstance() {
  6. if (uniqueInstance == null) {
  7. uniqueInstance = new Singleton();
  8. }
  9. return uniqueInstance;
  10. }
  11. }

懒汉式-线程安全

使用锁的方式,保证只有一个线程进入该方法。但是当一个线程进入该方法之后,其它试图进入该方法的线程都必须等待,因此性能上有一定的损耗。

public static synchronized Singleton getUniqueInstance() {
    if (uniqueInstance == null) {
        uniqueInstance = new Singleton();
    }
    return uniqueInstance;
}

双重校验锁模式-线程安全

双重校验先判断getUniqueInstance是否初始化,如果已经初始化则不用加锁,若没有初始化则对初始化代码进行加锁。
使用两层if的目的:如果有两个线程同时进入第一层判断,并且都成功往下走,虽然有同步锁,如果没有第二层判断,则都会进行一次实例化操作,只是先后问题。
使用 volatile 可以禁止 JVM 的指令重排,保证在多线程环境下也能正常运行,否则其他线程可能会拿到一个还未初始化的实例。

public class Singleton {

    private volatile static Singleton uniqueInstance;

    private Singleton() {
    }

    public static Singleton getUniqueInstance() {
        if (uniqueInstance == null) {
            synchronized (Singleton.class) {
                if (uniqueInstance == null) {
                    uniqueInstance = new Singleton();
                }
            }
        }
        return uniqueInstance;
    }
}

饿汉式-线程安全

该方法是线程安全的,但也丢失了延迟实例化带来的节约资源的好处。

private static Singleton uniqueInstance = new Singleton();

静态内部类实现

当 Singleton 类加载时,静态内部类 SingletonHolder 没有被加载进内存。只有当调用 getUniqueInstance() 方法从而触发 SingletonHolder.INSTANCE 时 SingletonHolder 才会被加载,此时初始化 INSTANCE 实例。

public class Singleton {

    private Singleton() {
    }

    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }

    public static Singleton getUniqueInstance() {
        return SingletonHolder.INSTANCE;
    }
}

枚举类实现

这是单例模式的最佳实践,它实现简单,并且在面对复杂的序列化或者反射攻击的时候,能够防止实例化多次。

public enum Singleton {
    uniqueInstance;
}