MENU

《Java设计模式及实践》第二章读书笔记 单例模式

May 15, 2022 • 《Java设计模式及实践》

单例模式

单例模式,顾名思义,用来保证一个对象只能创建一个实例,是最常用的设计模式,它具有易于理解、使用简便等特点,但是用不好的话也会造成弊大于利的后果。

1. 普通单例模式

最普通的单例模式,但只适合单线程编程中。

/**
 * 最普通的单例模式
 */
public class SimpleSingleton {

    private static SimpleSingleton instance;

    public SimpleSingleton() {
    }

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

    //...

    public static void main(String[] args) {
        SimpleSingleton.getInstance();
    }
}

2. 拥有双重校验锁机制的同步锁单例模式

存在这样一种情况,一个线程首先使用新构造器实例化单例对象,同时又有一个线程获取单例时判断单例实例是否为空,此时第一个线程还没有完成实例化操作,所以第二个线程会发现这个实例为空的,并且也开始实例化单例对象。

这种情况看似发生的概率很小,但是当创建单例对象需要较长时间的情况下,这个情况发生的概率就会变高。

/**
 * 拥有双重校验锁机制的同步锁单例模式
 */
public class DoubleLockSingleton {

    private static DoubleLockSingleton instance;

    public DoubleLockSingleton() {
    }

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

    //...

    public static void main(String[] args) {
        DoubleLockSingleton.getInstance();
    }
}

3. 无锁的线程安全单例模式

利用类加载的性质,直接在声明时实例化静态成员来保证一个类只有一个实例。这种方式避免了使用同步锁机制和判断实例是否被创建的额外检查。

/**
 * 无锁的线程安全单例模式
 */
public class LockFreeSingleton {

    private static final LockFreeSingleton instance = new LockFreeSingleton();

    public LockFreeSingleton() {
    }

    public static LockFreeSingleton getInstance() {
        return instance;
    }

    //...

    public static void main(String[] args) {
        LockFreeSingleton.getInstance();
    }
}

关于提前加载和延迟加载

按照实例对象被创建的时机,可以将单例模式分为两类。

  1. 提前加载单例模式:应用开始时创建单例实例。
  2. 延迟加载单例模式:在getInstance方法首次被调用时才调用单例构造器创建实例。

上面说到的无锁的线程安全单例模式在早期版本的java中属于提前加载单例模式,但是后来版本中,类只有在使用时才会被加载,这是就成为了延迟加载单例模式。

这主要取决于JVM虚拟机的实现机制,所以在设计时要避免与JVM的实现机制进行绑定。

在虚拟机执行参数中增加-XX:+TraceClassLoading参数,可以监控类的加载。

Sundae97/Design-Patterns-Java