设计模式六之单例模式

本文详细介绍了单例模式的定义、特点、结构与实现方式,包括懒汉模式、双重检查、静态内部类、饿汉式、防止序列化破坏、反射破坏及枚举实现等。并列举了开源软件如`java.lang.Runtime`、`org.apache.ibatis.executor.ErrorContext`中的单例应用。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

  有些系统中,为了节省内存资源,保证数据内容的一致性,对某些类要求只能创建一个实例,这就是所谓的单例模式。

1. 模式的定义与特点

1.1 模式的定义

  单例模式(Singleton):一个类只有一个实例,且该类能自行创建这个实例的一种模式。在计算机系统中,多线程中的线程池、应用程序的日志对象、数据库的连接池、网站的计数器、Web 应用的配置对象和系统中的缓存等常常被设计成单例模式。

1.2 模式的特点

  单例模式的特点有:
    1. 单例类只有一个实例;;
    2. 该单例对象必须由该单例类自行创建;
    2. 单例类对外提供一个访问该单例类的全局访问点。

2. 模式的结构与实现

  单例模式是设计模式中最简单的模式之一,通常,普通类的构造函数都是公有的,外部类可以通过 new 构造函数 来生成多个实例。但是,如果该类的构造函数设为私有的,外部类就无法调用该构造函数,也就无法生成多个实例。这时候该类就必须定义一个静态私有实例,并向外部提供一个静态公有的函数用于创建或获取该静态私有实例。

2.1 模式的结构

  单例模式的主要结构为:
    1.单例类:包含一个实例且能自行创建这个实例的类;
    2. 访问类:使用单例的类;

单例模式类UML

2.2 模式的实现

  单例模式的实现主要考虑到以下几点:私有构造器、线程安全、延迟加载、序列化和反序列化安全和反射。

2.2.1 懒汉模式
/**
 * 懒加载单例
 */
public class LazySingleton implements Serializable  {

    /**
     * 静态私有实例
     */
    private static LazySingleton lazySingleton = null;

    private LazySingleton() {

    }

    /**
     * 创建或获取静态私有实例的公有静态函数
     * @return
     */
    public synchronized static LazySingleton getInstance() {
        if (lazySingleton == null) {
            lazySingleton = new LazySingleton();
        }
        return lazySingleton;
    }

    /**
     * 防止序列化和反序列化破坏单例,单例类必须实现 Serializable 序列化接口
     * @return
     */
    public Object readResolve() {
        return instance;
    }
}

  该单例懒汉模式提供了一个私有静态实例和一个创建或获取私有实例的公有方法以及一个私有的构造器,同时满足延迟加载,但是使用 Synchronized 修饰获取实例的方法,性能不佳,因此需要进一步的优化。

2.2.2 双重检查(Double-Check)
/**
 * 懒加载双重检查单例
 */
public class LazyDoubleCheckSingleton implements Serializable  {

    /**
     * 静态私有实例且用volatile修饰保证可见性
     */
    private volatile static LazyDoubleCheckSingleton lazyDoubleCheckSingleton = null; 1

    private LazyDoubleCheckSingleton() {

    }

    /**
     * 创建或获取静态私有实例的公有静态函数
     * @return
     */
    public static LazyDoubleCheckSingleton getInstance() {
        if (lazyDoubleCheckSingleton == null) {
            synchronized (LazyDoubleCheckSingleton.class) {
                if (lazyDoubleCheckSingleton == null) {
                    lazyDoubleCheckSingleton = new LazyDoubleCheckSingleton(); 2
                }
            }
        }
        return lazyDoubleCheckSingleton;
    }

    /**
     * 防止序列化和反序列化破坏单例,单例类必须实现 Serializable 序列化接口
     * @return
     */
    public Object readResolve() {
        return instance;
    }
}

  使用双重检查实现懒加载单例有两个点需要注意:1 为什么需要两个 if 判断 2 为什么需要 实现 volatile 修饰实例变量?这里需要说明一下。
  使用两个 if 双重检查是因为两个线程同时进入 getInstance() 方法,都要进行 instantce == null 的判断,然后有线程1获取了 synchronized 锁 开始执行 代码 2 (即23行),进行对象实例的创建后释放锁,此时线程2如果不再次进行 instantce == null 的判断,线程2就会再次进行对象实例的创建,因此 代码23行处的判处是非常有必要的。
  使用 volatile 修饰实例变量是为了防止重排序,保证可见性,代码23行处共执行3条命令:1 分配对象的内存空间;2 初始化对象;3 设置变量指向刚刚分配的内存地址。但是在 步骤2 和步骤3 之间顺序不固定,有时候步骤2 先执行,有时候步骤3 先执行,因此如果线程1先执行步骤3就释放锁,线程2 判断 instance != null 后直接返回的就是空对象,因此需要使用 volatile 防止重排序,保证可见性。想要深入的了解 volatile 关键字的是如何保证可见性和有序性,可以参考《并发编程之 volatile 关键字 - 解决可见行和有序性》

声明为volatile的变量可以做到一下保证:
   1. 其他线程对变量的修改,可以及时的反应在当前线程中。
   2. 确保当前线程对变量的修改,能及时写回共享内存中,并被其他线程所见
   3. 使用volatile声明的变量,编译器会保证其有序性。

单例 Double-Check

2.2.3 静态内部类
/**
 * 静态内部类
 */
public class StaticInnerClassSingleton implements Serializable {

    private StaticInnerClassSingleton() {

    }

    public static StaticInnerClassSingleton getInstance() {
        return InnerClass.staticInnerClassSingleton;
    }

    /**
     * 静态内部类,创建单例类实例
     */
    private static class InnerClass {
        private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }

    /**
     * 防止序列化和反序列化破坏单例,单例类必须实现 Serializable 序列化接口
     * @return
     */
    public Object readResolve() {
        return instance;
    }
}

  在双重检查机制中,使用 volatile 防止重排序,保证内存可见性。使用静态内部类是在创建对象时,对象初始化和变量指向内存地址顺序变化时,对线程不可见。这种方式利用了 classloader 机制来保证初始化 instance 时只有一个线程,Singleton 类被装载了,instance 不一定被初始化。因为 InnerClass 类没有被主动使用,只有通过显式调用 getInstance 方法时,才会显式装载 InnerClass 类,从而实例化 instance,同时确保了 intance 实例延迟加载。
单例静态内部类

2.2.4 饿汉式单例
/**
 * 饿汉式单例
 */
public class HungrySingleton implements Serializable {

    /**
     * 静态不变实例
     */
    private final static HungrySingleton instance = new HungrySingleton();

    /**
     * 私有构造器
     */
    private HungrySingleton() {
        
    }

    /**
     * 静态方法提供获取单例类实例
     * @return
     */
    public static HungrySingleton getInstance() {
        return instance;
    }
    
    /**
     * 防止序列化和反序列化破坏单例,单例类必须实现 Serializable 序列化接口
     * @return
     */
    public Object readResolve() {
        return instance;
    }
}

  饿汉式单例比较简单,就是在类初始化的时候创建实例,因此没有延迟加载,在实例加载过程复杂的情况下,类一直没有被使用,就会造成内存浪费。

2.2.5 防止序列化和反序列化破坏单例
public class Client {

    public static void main(String[] args) throws Exception {
        HungrySingleton instance = HungrySingleton.getInstance();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singleton_file"));
        HungrySingleton hungrySingleton = (HungrySingleton) ois.readObject();

        System.out.println(instance);
        System.out.println(hungrySingleton);
        System.out.println(instance == hungrySingleton);
    }
}

运行结果如下:
com.baidu.test.disign.creational.singleton.HungrySingleton@d716361
com.baidu.test.disign.creational.singleton.HungrySingleton@34c45dca
false

  在使用对象流进行写入和读取的时候会发现,单例类被破坏了,写入和读取的对象发生了改变,这就违反了单例只有一个实例的原则。现在我们就来防止序列化和反序列化破坏单例。我们已饿汉式举例:

/**
 * 饿汉式单例,防止序列化破坏
 */
public class HungrySingleton implements Serializable {

    /**
     * 静态不变实例
     */
    private final static HungrySingleton instance = new HungrySingleton();

    /**
     * 私有构造器
     */
    private HungrySingleton() {

    }

    /**
     * 静态方法提供获取单例类实例
     * @return
     */
    public static HungrySingleton getInstance() {
        return instance;
    }

    /**
     * 防止序列化和反序列化破坏单例,单例类必须实现 Serializable 序列化接口
     * @return
     */
    public Object readResolve() {
        return instance;
    }
}

# 再次执行 client 类,其执行结果为:
com.baidu.test.disign.creational.singleton.HungrySingleton@d716361
com.baidu.test.disign.creational.singleton.HungrySingleton@d716361
true

  在这里我们会发现,单例类实现 Serializable 序列化接口,然后在添加一个 readResolve 方法,其得到的单例类永远就式一个,这是为什么呢?同时为什么就是 readResolve 方法呢?下面我们来看一下:

public class ObjectInputStream
    extends InputStream implements ObjectInput, ObjectStreamConstants
{
    public final Object readObject() throws IOException, ClassNotFoundException {
        try {
            Object obj = readObject0(false); // 1.调用了 readObject0 方法
            return obj;
        } finally {
            passHandle = outerHandle;
            if (closed && depth == 0) {
                clear();
            }
        }
    }
    
    /**
     * Underlying readObject implementation.
     */
    private Object readObject0(boolean unshared) throws IOException {
        try {
            switch (tc) {
                case TC_OBJECT:
                    return checkResolve(readOrdinaryObject(unshared)); // 2.调用了readOrdinaryObject 方法
            }
        } finally {
            depth--;
            bin.setBlockDataMode(oldMode);
        }
    }
    
    private Object readOrdinaryObject(boolean unshared) throws IOException {
        Object obj;
        try {
            obj = desc.isInstantiable() ? desc.newInstance() : null; // 3.进行了 desc.isInstantiable() 判断
        } catch (Exception ex) {
            throw (IOException) new InvalidClassException(
                desc.forClass().getName(),
                "unable to create instance").initCause(ex);
        }
    
        if (obj != null &&
            handles.lookupException(passHandle) == null &&
            desc.hasReadResolveMethod()) // 4.进行了 desc.hasReadResolveMethod() 判断
        {
            Object rep = desc.invokeReadResolve(obj); // 5.执行了 desc.invokeReadResolve(obj) 方法
            if (unshared && rep.getClass().isArray()) {
                rep = cloneArray(rep);
            }
            if (rep != obj) {
                // Filter the replacement object
                if (rep != null) {
                    if (rep.getClass().isArray()) {
                        filterCheck(rep.getClass(), Array.getLength(rep));
                    } else {
                        filterCheck(rep.getClass(), -1);
                    }
                }
                handles.setObject(passHandle, obj = rep); // 6. rep 赋值 obj
            }
        }
    
        return obj;
    }
}
public class ObjectStreamClass implements Serializable {
    
    /** 是否实现 serializable/externalizable
     * Returns true if represented class is serializable/externalizable and can
     * be instantiated by the serialization runtime--i.e., if it is
     * externalizable and defines a public no-arg constructor, or if it is
     * non-externalizable and its first non-serializable superclass defines an
     * accessible no-arg constructor.  Otherwise, returns false.
     */
    boolean isInstantiable() {
        requireInitialized();
        return (cons != null);
    }
    
    /** 是否定义了 readResolve 方法
     * Returns true if represented class is serializable or externalizable and
     * defines a conformant readResolve method.  Otherwise, returns false.
     */
    boolean hasReadResolveMethod() {
        requireInitialized();
        return (readResolveMethod != null);
    }
    
    /** 反射执行 readResolve 方法
     * Invokes the readResolve method of the represented serializable class and
     * returns the result.  Throws UnsupportedOperationException if this class
     * descriptor is not associated with a class, or if the class is
     * non-serializable or does not define readResolve.
     */
    Object invokeReadResolve(Object obj) throws IOException, UnsupportedOperationException {
        requireInitialized();
        if (readResolveMethod != null) {
            try {
                return readResolveMethod.invoke(obj, (Object[]) null); 
            } catch (InvocationTargetException ex) {
                Throwable th = ex.getTargetException();
                if (th instanceof ObjectStreamException) {
                    throw (ObjectStreamException) th;
                } else {
                    throwMiscException(th);
                    throw new InternalError(th);  // never reached
                }
            } catch (IllegalAccessException ex) {
                // should not occur, as access checks have been suppressed
                throw new InternalError(ex);
            }
        } else {
            throw new UnsupportedOperationException();
        }
    }
}

  根据上面的源码我们能够看到,当单例类实现了序列化接口的同时声明了 readResolve() 方法,那么在序列化和反序列化的时候,虽然过程也创建了多个对象,但最终返回的单例类实例永远只有一个。

2.2.6 通过反射破坏单例
public class Client {

    public static void main(String[] args) throws Exception {
        Class<HungrySingleton> hungrySingletonClass = HungrySingleton.class;
        Constructor<HungrySingleton> constructor = hungrySingletonClass.getDeclaredConstructor();
        constructor.setAccessible(true);
        HungrySingleton hungrySingleton = constructor.newInstance();

        HungrySingleton instance = HungrySingleton.getInstance();
        System.out.println(instance);
        System.out.println(hungrySingleton);
        System.out.println(instance == hungrySingleton);
    }
}

# 运行结果如下:
com.baidu.test.disign.creational.singleton.HungrySingleton@66d3c617
com.baidu.test.disign.creational.singleton.HungrySingleton@63947c6b
false

  上面的结果我们可以看出,以上的单例模式实现我们都可以通过反射来破坏单例的唯一性,那么我们该如何防止利用反射来破坏单例呢?

/**
 * 饿汉式单例
 */
public class HungrySingleton implements Serializable {

    /**
     * 静态不变实例
     */
    private final static HungrySingleton instance = new HungrySingleton();

    /**
     * 私有构造器
     */
    private HungrySingleton() {
        if (instance == null) {
            throw new RuntimeException("不允许利用反射破坏单例"); // 这里因为是类加载时就创建了单例
        }
    }

    /**
     * 静态方法提供获取单例类实例
     * @return
     */
    public static HungrySingleton getInstance() {
        return instance;
    }

    /**
     * 防止序列化和反序列化破坏单例,单例类必须实现 Serializable 序列化接口
     * @return
     */
    public Object readResolve() {
        return instance;
    }
}
/**
 * 静态内部类
 */
public class StaticInnerClassSingleton implements Serializable {

    private StaticInnerClassSingleton() {
        if (InnerClass.staticInnerClassSingleton != null) {
            throw new RuntimeException("不允许利用反射破坏单例"); // 因为 InnerClass 初始化创建单例类
        }
    }

    public static StaticInnerClassSingleton getInstance() {
        return InnerClass.staticInnerClassSingleton;
    }

    /**
     * 静态内部类,创建单例类实例
     */
    private static class InnerClass {
        private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }

    /**
     * 防止序列化和反序列化破坏单例,单例类必须实现 Serializable 序列化接口
     * @return
     */
    public Object readResolve() {
        return instance;
    }
}

  再次执行 client 代码,会发现成功的防止了利用反射来破坏单例。

Exception in thread "main" java.lang.ExceptionInInitializerError
	at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
	at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:62)
	at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
	at java.lang.reflect.Constructor.newInstance(Constructor.java:423)
	at com.baidu.test.disign.creational.singleton.Client.main(Client.java:33)
Caused by: java.lang.RuntimeException: 不允许利用反射破坏单例
	at com.baidu.test.disign.creational.singleton.HungrySingleton.<init>(HungrySingleton.java:20)
	at com.baidu.test.disign.creational.singleton.HungrySingleton.<clinit>(HungrySingleton.java:13)

  这里我们防止利用反射来破坏单例也仅仅是有限的防止,在延迟加载实现的单例类中,无法做到在私有构造器中进行判断,因为延迟加载,在反射和正常获取单例类未知顺序的情况下,无法作出判处。

2.2.7 枚举单例
/**
 * 枚举单例
 */
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;
    }
}

  枚举实现的单例是实现单例模式的最佳方法。它更简洁,自动支持序列化机制,绝对防止多次实例化。这种方式是 Effective Java 作者 Josh Bloch 提倡的方式,它不仅能避免多线程同步问题,而且还自动支持序列化机制,防止反序列化重新创建新的对象,绝对防止多次实例化。下面我们来看看编译后的枚举单例类,为什么是提倡的方式?

public final class EnumSingleton extends Enum {

    public static EnumSingleton[] values()
    {
        return (EnumSingleton[])$VALUES.clone();
    }

    public static EnumSingleton valueOf(String name)
    {
        return (EnumSingleton)Enum.valueOf(com/baidu/test/disign/creational/singleton/EnumSingleton, name);
    }

    private EnumSingleton(String s, int i) // 1.私有构造器
    {
        super(s, i);
    }

    public Object getData()
    {
        return data;
    }

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

    public static EnumSingleton getInstance() //2.提供全局创建或获取实例方法
    {
        return INSTANCE;
    }

    public static final EnumSingleton INSTANCE; //3.静态不变的实例变量
    private Object data;
    private static final EnumSingleton $VALUES[];

    static //4.静态代码块初始化单例类
    {
        INSTANCE = new EnumSingleton("INSTANCE", 0);
        $VALUES = (new EnumSingleton[] {
            INSTANCE
        });
    }
}

  通过查看 jad 编译后的单例类后我们可以看到,代码1、2、3、4 处完成符合单例类的要求,而且有点类似于饿汉式单例,那么我们用序列化和反射来破坏枚举单例类,看看结果如何?

public class Client {

    public static void main(String[] args) throws Exception {
        EnumSingleton instance = EnumSingleton.getInstance();
        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
        oos.writeObject(instance);
        ObjectInputStream ois = new ObjectInputStream(new FileInputStream("singleton_file"));
        EnumSingleton enumSingleton = (EnumSingleton) ois.readObject();

        System.out.println(instance);
        System.out.println(enumSingleton);
        System.out.println(instance == enumSingleton);

        Class<EnumSingleton> enumSingletonClass = EnumSingleton.class;
        Constructor<EnumSingleton> constructor = enumSingletonClass.getDeclaredConstructor(String.class, Integer.class);
        constructor.setAccessible(true);
        EnumSingleton enumSingleton1 = constructor.newInstance("tom", 20);

        EnumSingleton enumSingleton2 = EnumSingleton.getInstance();
        System.out.println(enumSingleton1);
        System.out.println(enumSingleton2);
        System.out.println(enumSingleton1 == enumSingleton2);
    }
} 

# 运行结果如下:
INSTANCE
INSTANCE
true

Exception in thread "main" java.lang.IllegalArgumentException: Cannot reflectively create enum objects
	at java.lang.reflect.Constructor.newInstance(Constructor.java:417)
	at com.baidu.test.disign.creational.singleton.Client.main(Client.java:57)

  我们可以看到通过序列化破坏单例是没有效果的,而通过反射破坏就直接报错,说没有方法,下面我们来通过源码解释一下这两个原因。

public class ObjectInputStream {
    /**
     * Reads in and returns enum constant, or null if enum type is
     * unresolvable.  Sets passHandle to enum constant's assigned handle.
     */
    private Enum<?> readEnum(boolean unshared) throws IOException {
        String name = readString(false);
        Enum<?> result = null;
        Class<?> cl = desc.forClass();
        if (cl != null) {
            try {
                @SuppressWarnings("unchecked")
                Enum<?> en = Enum.valueOf((Class)cl, name); // 这里直接通过 Enum.valueOf 获取实例
                result = en;
            } catch (IllegalArgumentException ex) {
                throw (IOException) new InvalidObjectException(
                    "enum constant " + name + " does not exist in " +
                    cl).initCause(ex);
            }
            if (!unshared) {
                handles.setObject(enumHandle, result);
            }
        }
    
        handles.finish(enumHandle);
        passHandle = enumHandle;
        return result;
    }
}
public final class Constructor<T> extends Executable {
   public T newInstance(Object ... initargs)
    throws InstantiationException, IllegalAccessException,
           IllegalArgumentException, InvocationTargetException
    {
        if (!override) {
            if (!Reflection.quickCheckMemberAccess(clazz, modifiers)) {
                Class<?> caller = Reflection.getCallerClass();
                checkAccess(caller, clazz, null, modifiers);
            }
        }
        if ((clazz.getModifiers() & Modifier.ENUM) != 0) // 这里说明了不能通过反射创建枚举类
            throw new IllegalArgumentException("Cannot reflectively create enum objects");
        ConstructorAccessor ca = constructorAccessor;   // read volatile
        if (ca == null) {
            ca = acquireConstructorAccessor();
        }
        @SuppressWarnings("unchecked")
        T inst = (T) ca.newInstance(initargs);
        return inst;
    } 
}
2.2.8 容器单例
/**
 * 容器单例
 */
public class ContainerSingleton {

    private static Map<String, Object> singletonMap = new HashMap<>();

    private ContainerSingleton() {

    }

    public static void putInstance(String key, Object instance) {
        if (StringUtils.isNotBlank(key) && instance != null) {
            if (!singletonMap.containsKey(key)) {
                singletonMap.put(key, instance);
            }
        }
    }

    public static Object getInstance(String key) {
        return singletonMap.get(key);
    }

}

  我们可以在系统初始化的时候,把所有的单例对象都放入容器当中,该容器类的线程安全可视具体的情景设定。

2.2.9 ThreadLocal 线程单例
/**
 * ThreadLocal 线程单例
 */
public class ThreadLocalSingleton {

    private static final ThreadLocal<ThreadLocalSingleton> threadLocalSingletonInstance
            = ThreadLocal.withInitial(() -> new ThreadLocalSingleton());

    private ThreadLocalSingleton() {

    }

    public static ThreadLocalSingleton getInstance() {
        return threadLocalSingletonInstance.get();
    }

}

  客户端测试

public class Client {

    public static void main(String[] args) throws Exception {
        System.out.println("main thread: " + ThreadLocalSingleton.getInstance());
        System.out.println("main thread: " + ThreadLocalSingleton.getInstance());
        System.out.println("main thread: " + ThreadLocalSingleton.getInstance());
        System.out.println("main thread: " + ThreadLocalSingleton.getInstance());
        System.out.println("main thread: " + ThreadLocalSingleton.getInstance());

        new Thread(() -> {
            ThreadLocalSingleton instance = ThreadLocalSingleton.getInstance();
            System.out.println(Thread.currentThread().getName() + " " + instance);
        }).start();

        new Thread(() -> {
            ThreadLocalSingleton instance = ThreadLocalSingleton.getInstance();
            System.out.println(Thread.currentThread().getName() + " " + instance);
        }).start();
    }

}

# 运行结果如下:
main thread: com.baidu.test.disign.creational.singleton.ThreadLocalSingleton@66d3c617
main thread: com.baidu.test.disign.creational.singleton.ThreadLocalSingleton@66d3c617
main thread: com.baidu.test.disign.creational.singleton.ThreadLocalSingleton@66d3c617
main thread: com.baidu.test.disign.creational.singleton.ThreadLocalSingleton@66d3c617
main thread: com.baidu.test.disign.creational.singleton.ThreadLocalSingleton@66d3c617
Thread-0 com.baidu.test.disign.creational.singleton.ThreadLocalSingleton@291e1298
Thread-1 com.baidu.test.disign.creational.singleton.ThreadLocalSingleton@3c86690c

  从上面的运行结果,我们可以看到使用 ThreadLocal 形式的单例不是严格意义上的单例,而是线程间的单例,多线程之间保持之间的单例类实例,同一线程间维护一个实例。

3. 模式在开源软件中的应

3.1 java.lang.Runtime

public class Runtime {
    private static Runtime currentRuntime = new Runtime();

    /**
     * Returns the runtime object associated with the current Java application.
     * Most of the methods of class <code>Runtime</code> are instance
     * methods and must be invoked with respect to the current runtime object.
     *
     * @return  the <code>Runtime</code> object associated with the current
     *          Java application.
     */
    public static Runtime getRuntime() {
        return currentRuntime;
    }

    /** Don't let anyone else instantiate this class */
    private Runtime() {}
 }   

  Runtime 就是典型的饿汉式单例,单例类在初始化是完成实例的创建,同时私有构造器和提供对外全局访问点。

3.2 org.apache.ibatis.executor.ErrorContext

public class ErrorContext {

  private static final ThreadLocal<ErrorContext> LOCAL = new ThreadLocal<ErrorContext>();

  private ErrorContext() {
  }

  public static ErrorContext instance() {
    ErrorContext context = LOCAL.get();
    if (context == null) {
      context = new ErrorContext();
      LOCAL.set(context);
    }
    return context;
  }
}  

  mybaties 中的 ErrorContext 就是使用 ThreadLocal 线程单例模式,保证同一线程实例唯一。

3.3 org.springframework.beans.factory.suppor.AbstractBeanFactory

public abstract class AbstractBeanFactory extends FactoryBeanRegistrySupport implements ConfigurableBeanFactory {
    
    public Object getBean(String name) throws BeansException {
        return this.doGetBean(name, (Class)null, (Object[])null, false);
    }
    
    protected <T> T doGetBean(
      final String name, final Class<T> requiredType, final Object[] args, boolean typeCheckOnly)
      throws BeansException {

       final String beanName = transformedBeanName(name);
       Object bean;
    
       // Eagerly check singleton cache for manually registered singletons.
       Object sharedInstance = getSingleton(beanName);
       
       return (T) bean;
    }
    
    // 类似与 Double-Check 双重检查单例
    protected Object getSingleton(String beanName, boolean allowEarlyReference) {
       Object singletonObject = this.singletonObjects.get(beanName);
       if (singletonObject == null && isSingletonCurrentlyInCreation(beanName)) {
          synchronized (this.singletonObjects) {
             singletonObject = this.earlySingletonObjects.get(beanName);
             if (singletonObject == null && allowEarlyReference) {
                ObjectFactory<?> singletonFactory = this.singletonFactories.get(beanName);
                if (singletonFactory != null) {
                   singletonObject = singletonFactory.getObject();
                   this.earlySingletonObjects.put(beanName, singletonObject);
                   this.singletonFactories.remove(beanName);
                }
             }
          }
       }
       return (singletonObject != NULL_OBJECT ? singletonObject : null);
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值