前言
整个系统中只会出现要给实例,比如Spring中的Bean基本都是单例的
UML类图
无
代码示例
java
package com.sw.learn.pattern.B_create.c_singleton;
public class Main {
public static void main(String[] args) {
// double check locking 线程安全+懒加载 ⭐️
// SingletonDCL instance = SingletonDCL.getInstance();
// 静态内部类 线程安全+懒加载 ⭐️⭐️
// Singleton s1 = Singleton.getInstance();
// Singleton s2 = Singleton.getInstance();
// System.out.println(s1 == s2); // true
// 枚举类 线程安全+饿汉式 ⭐️⭐️⭐️
// SingletonEnum.INSTANCE.doSomething();
// 基于枚举内部类的懒汉式单例模式 ⭐️⭐️⭐️⭐️
BeanContainer instance2 = BeanContainer.getInstance();
BeanContainer instance3 = BeanContainer.getInstance();
System.out.println(instance2 == instance3); // true
}
}
class Singleton {
// 私有构造方法,防止外部实例化
private Singleton() {
System.out.println("Singleton constructor called.");
}
// 静态内部类,只有在第一次调用 getInstance() 时才会加载
private static class Holder {
private static final Singleton INSTANCE = new Singleton();
}
// 对外提供获取实例的方法
public static Singleton getInstance() {
return Holder.INSTANCE;
}
}
class SingletonDCL {
// 使用 volatile 防止初始化时指令重排序,确保线程安全
private static volatile SingletonDCL instance;
private SingletonDCL() {
System.out.println("SingletonDCL constructor called.");
}
public static SingletonDCL getInstance() {
if (instance == null) { // 第一次检查(不加锁) 提高性能
synchronized (SingletonDCL.class) {
if (instance == null) { // 第二次检查(加锁后再检查) 防止多个线程同时通过第一层检查后并发创建;
instance = new SingletonDCL();
}
}
}
return instance;
}
}
enum SingletonEnum {
INSTANCE;
public void doSomething() {
System.out.println("Doing something with enum singleton.");
}
}
// 枚举编译后效果
//final class SingletonEnum2 extends Enum<SingletonEnum2> {
// public static final SingletonEnum2 INSTANCE = new SingletonEnum2("INSTANCE", 0);
//
// private SingletonEnum2(String name, int ordinal) {
// super(name, ordinal);
// }
//}
class BeanContainer {
// 调用getInstance方法时,第一次初始化ContainerHolder枚举类,初始的时候执行构造方法,实例化BeanContainer对象
/**
* 第一步:调用 ContainerHolder.HOLDER
* JVM 首次访问 ContainerHolder 时会触发类加载。
* <p>
* 内部类不会在外部类加载时加载,只有在首次访问时才加载(这就是懒加载)。
* <p>
* 枚举类加载也是线程安全的,JVM 会保证枚举类只加载一次,不会有并发初始化问题。
*
* @return
*/
public static BeanContainer getInstance() {
return ContainerHolder.HOLDER.instance;
}
private enum ContainerHolder {
HOLDER; // 容器,装载了BeanContainer对象
/**
* 第二步:加载 enum ContainerHolder
* 枚举类 ContainerHolder 加载时,会创建一个 enum 常量 HOLDER。
*/
private BeanContainer instance;
ContainerHolder() {
instance = new BeanContainer(); // 会被执行一次,初始化 instance, 即 ContainerHolder.HOLDER 被加载时。
}
}
}