专业编程基础技术教程

网站首页 > 基础教程 正文

单例模式(Singleton)(单例模式懒汉和饿汉)

ccvgpt 2024-07-28 12:11:21 基础教程 8 ℃

核心作用

保证一个类只有一个实例,并且对外提供一个访问该实例的全局访问点。

优点

  • 由于单例模式只生成一个实例,减少系统性能开销;
  • 单例模式可以在系统设置全局访问点,优化环境共享资源访问;

常用应用场景

需要频繁的创建和销毁的对象,创建对象耗时过多或者消耗资源过多(即重量级对象),但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如:数据源、Session工厂等)

单例模式(Singleton)(单例模式懒汉和饿汉)

  1. windows 系统的任务管理器、回收站;
  2. 项目中读取配置文件的类,一般配置文件只需要读取一次,所以只需要一个读取配置文件的类;
  3. 网站的计数器
  4. 应用程序中日志应用,一般都是单例模式实现,由于共享的日志文件一直处于打开状态,只能有一个实例操作,否则不好追加
  5. 数据库的连接池设计
  6. 操作系统的文件系统
  7. 在 Spring 中,每个 bean 默认是单例,这样做的优点是 Spring 容器很好的管理
  8. 在 Servlet 编程中,每个 Servlet 也是单例
  9. 在 SpringMVC 中,控制器对象也是单例

核心步骤

  1. 私有化构造方法
  2. 提供私有的静态的对象的实例属性
  3. 提供对外访问的静态方法

八种单例模式

  1. 饿汉式(静态常量):线程安全,调用效率高,但是不能延时加载;
  2. 饿汉式(静态代码块):线程安全,调用效率高,但是不能延时加载;
  3. 懒汉式(普通懒汉式):线程不安全;
  4. 懒汉式(同步方法):线程安全,调用效率不高,但是可以延时加载;
  5. 懒汉式(同步代码块):线程不安全;
  6. 双重检测锁式:起到懒加载的效果,线程也安全,比同步方法效率高一些。比同步方法效率高一些,双重检查保证线程安全,由于 JVM 底层内部模型原因,偶尔会出现问题,需要使用 volatile 防止指令重排;
  7. 静态内部类式:线程安全,调用效率高,可以延时加载;
  8. 枚举单例:线程安全,调用效率高,不能延时加载,实现简单。由于 JVM 从根本上提供保障,避免通过反射和反序列化的漏洞;

两种饿汉式

【不推荐】饿汉式(静态常量)

package top.simba1949.singleton.pattern;

/**
 * 饿汉式——静态常量
 * 基本步骤:
 * 1. 构造器私有化
 * 2. 在类的内部创建对象
 * 3. 向外暴露一个静态的公共方法
 * <p>
 * 优缺点
 * 优点:写法简单,在类装载的时候完成实例化,避免多线程问题
 * 缺点:没有进行懒加载,如果没有使用到会造成内存浪费
 * <p>
 * 结论: 可以使用,但不推荐
 *
 * @author anthony
 * @date 2020/5/7 11:23
 */
public class HungrySingletonByStaticConstant {

    /**
     * 1. 构造器私有化
     */
    private HungrySingletonByStaticConstant() {
    }

    /**
     * 2. 在类的内部创建对象
     */
    private final static HungrySingletonByStaticConstant instance = new HungrySingletonByStaticConstant();

    /**
     * 3. 向外暴露一个静态的公共方法
     *
     * @return
     */
    public static HungrySingletonByStaticConstant getInstance() {
        return instance;
    }
}

【不推荐】饿汉式(静态代码块)

package top.simba1949.singleton.pattern;

/**
 * 饿汉式——静态代码块
 * 基本步骤:
 * 1. 构造器私有化
 * 2. 在类的内部创建对象
 * 3. 向外暴露一个静态的公共方法
 * <p>
 * 优缺点
 * 优点:写法简单,在类装载的时候完成实例化,避免多线程问题
 * 缺点:没有进行懒加载,如果没有使用到会造成内存浪费
 * <p>
 * 结论: 可以使用,但不推荐
 *
 * @Author anthony
 * @Date 2020/5/7 11:23
 */
public class HungrySingletonByStaticCodeBlock {
    /**
     * 1. 构造器私有化
     */
    private HungrySingletonByStaticCodeBlock() {
    }

    /**
     * 2. 在类的内部创建对象
     */
    private static HungrySingletonByStaticCodeBlock instance;

    static {
        // 静态代码块中,初始化
        instance = new HungrySingletonByStaticCodeBlock();
    }

    /**
     * 3. 向外暴露一个静态的公共方法
     *
     * @return
     */
    public static HungrySingletonByStaticCodeBlock getInstance() {
        return instance;
    }
}

三种懒汉式

【禁用】懒汉式(普通懒汉式)

package top.simba1949.singleton.pattern.impl;

/**
 * 懒汉式——线程不安全
 * 基本步骤:
 * 1. 构造器私有化
 * 2. 在类的内部创建对象
 * 3. 向外暴露一个静态的公共方法
 * <p>
 * 优缺点:
 * 优点:起到懒加载的效果,但是只能用于单线程下使用
 * 缺点:多线程下,一个线程进入if (null == instance){}判断语句,还未创建对象,另一个线程也进入这个判断语句,此时会产生多个实例
 * <p>
 * 结论:禁用
 *
 * @Author anthony
 * @Date 2020/5/7 11:43
 */
public class LazySingletonByCommon {

    /**
     * 1. 构造器私有化
     */
    private LazySingletonByCommon() {
    }

    private static LazySingletonByCommon instance;

    /**
     * 2. 在类的内部创建对象
     * 3. 向外暴露一个静态的公共方法
     *
     * @return
     */
    public static LazySingletonByCommon getInstance() {
        if (null == instance) {
            instance = new LazySingletonByCommon();
        }
        return instance;
    }
}

【不推荐】懒汉式(同步方法)

package top.simba1949.singleton.pattern;

/**
 * 懒汉式——同步方法
 * 基本步骤:
 * 1. 构造器私有化
 * 2. 在类的内部创建对象
 * 3. 向外暴露一个静态的公共方法
 * <p>
 * 优缺点:
 * 优点:起到懒加载的效果,线程也安全,但是使用同步方法,效率低下
 * 缺点:使用同步方法效率低下
 * <p>
 * 结论:可以使用,但不推荐
 *
 * @Author anthony
 * @Date 2020/5/7 15:19
 */
public class LazySingletonBySyncMethod {
    /**
     * 1. 构造器私有化
     */
    private LazySingletonBySyncMethod() {
    }

    private static LazySingletonBySyncMethod instance;

    /**
     * 2. 在类的内部创建对象,使用 synchronized 进行加锁保证线程安全
     * 3. 向外暴露一个静态的公共方法
     *
     * @return
     */
    public synchronized static LazySingletonBySyncMethod getInstance() {
        if (null == instance) {
            instance = new LazySingletonBySyncMethod();
        }
        return instance;
    }
}

【禁用】懒汉式(同步代码块)

package top.simba1949.singleton.pattern;

/**
 * 懒汉式——同步代码块,线程不安全,禁用
 * 基本步骤:
 * 1. 构造器私有化
 * 2. 在类的内部创建对象
 * 3. 向外暴露一个静态的公共方法
 * <p>
 * 优缺点:
 * 优点:起到懒加载的效果,比同步方法效率高一些,但是线程不安全
 * 缺点:比同步方法效率高一些,但是这种同步并不能起到线程安全问题
 * <p>
 * 结论:禁用
 *
 * @Author anthony
 * @Date 2020/5/7 15:22
 */
public class LazySingletonBySyncCode {
    /**
     * 1. 构造器私有化
     */
    private LazySingletonBySyncCode() {
    }

    private static LazySingletonBySyncCode instance;

    /**
     * 2. 在类的内部创建对象
     * 3. 向外暴露一个静态的公共方法
     *
     * @return
     */
    public static LazySingletonBySyncCode getInstance() {
        if (null == instance) {
            synchronized (LazySingletonBySyncCode.class) {
                // 只要有多个线程进入到 if 语句块中,每个线程会依次执行new对象操作(线程不安全)
                instance = new LazySingletonBySyncCode();
            }
        }
        return instance;
    }
}

【推荐】双重检测锁式

package top.simba1949.singleton.pattern.impl;

/**
 * 懒汉式——双重检查(同步代码块)
 * 基本步骤:
 * 1. 构造器私有化
 * 2. 在类的内部创建对象
 * 3. 向外暴露一个静态的公共方法
 * <p>
 * 优缺点:
 * 优点:起到懒加载的效果,线程也安全,比同步方法效率高一些
 * 比同步方法效率高一些,双重检查保证线程安全,(由于 JVM 底层内部模型原因,偶尔会出现问题,需要使用 volatile 防止指令重排)
 * <p>
 * 结论:推荐使用
 *
 * @Author anthony
 * @Date 2020/5/9 9:59
 */
public class SingletonByDoubleCheckSync {
    /**
     * 1. 构造器私有化
     */
    private SingletonByDoubleCheckSync() {
    }

    /**
     * 2. 在类的内部创建对象
     * 3. 向外暴露一个静态的公共方法
     *
     * @return
     */
    private volatile static SingletonByDoubleCheckSync instance;

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

【推荐】静态内部类式

package top.simba1949.singleton.pattern;

/**
 * 懒汉式——静态内部类
 * 基本步骤:
 * 1. 构造器私有化
 * 2. 在类的内部创建对象
 * 3. 向外暴露一个静态的公共方法
 * <p>
 * 优缺点:
 * 优点:起到懒加载的效果,线程也安全(采用类装载机制保证初始化实例时只有一个线程),效率高
 * 静态内部类方式在SingletonSeven类被装载是不会立即实例化,调用 getInstance() 方法才会装载,从而完成单例实例化
 * 类的静态属性只会在第一次加载类时初始化,JVM底层保证了线程安全,在类进行初始化时,其他线程无法进入
 * <p>
 * 结论:推荐使用
 *
 * @Author anthony
 * @Date 2020/5/9 10:11
 */
public class SingletonByStaticInnerClass {
    /**
     * 1. 构造器私有化
     */
    private SingletonByStaticInnerClass() {
    }

    private static class Inner {
        /**
         * 写一个静态内部类,该类中有一个静态属性 SingletonByStaticInnerClass
         * 2. 在类的内部创建对象
         */
        private static final SingletonByStaticInnerClass INSTANCE = new SingletonByStaticInnerClass();
    }

    /**
     * 3. 向外暴露一个静态的公共方法
     *
     * @return
     */
    public static SingletonByStaticInnerClass getInstance() {
        return Inner.INSTANCE;
    }
}

【推荐】枚举单例

package top.simba1949.singleton.pattern.impl;

/**
 * 懒汉式——枚举
 * <p>
 * 优缺点:
 * 优点:线程安全,调用效率高,不能延时加载,实现简单。由于 JVM 从根本上提供保障,避免通过反射和反序列化的漏洞
 * <p>
 * 结论:推荐使用
 *
 * @Author anthony
 * @Date 2020/5/9 10:37
 */
public enum SingletonByEnum {
    INSTANCE;
}

防止破坏单例模式的三种方式

防止通过反射创建多个对象

package top.simba1949.singleton.pattern;

/**
 * 避免反射创建多个对象,在构造方法中判断如果实例属性不为 null 时,抛出异常即可
 *
 * @author anthony
 * @datetime 2020/5/9 10:51
 */
public class SingletonByReflect {

    private static final SingletonByReflect instance = new SingletonByReflect();

    private SingletonByReflect() {
        if (null != instance) {
            throw new RuntimeException();
        }
    }

    public static SingletonByReflect getInstance() {
        return instance;
    }
}

防止通过反序列化创建多个对象

详见 java-io 中的 ObjectInputStream 的对象;

package top.simba1949.singleton.pattern;

import java.io.Serializable;

/**
 * 添加 readResolve() 方法可以避免反序列化创建多个对象
 *
 * @author anthony
 * @datetime 2020/5/9 10:53
 */
public class SingletonBySerializable implements Serializable {
    private static final long serialVersionUID = 5155396228091341764L;

    private static final SingletonBySerializable instance = new SingletonBySerializable();

    private SingletonBySerializable() {
    }

    public static SingletonBySerializable getInstance() {
        return instance;
    }

    /**
     * 添加 readResolve() 方法可以避免反序列化创建多个对象
     *
     * @return
     */
    private Object readResolve() {
        return instance;
    }
}

防止通过clone创建多个对象

package top.simba1949.singleton.pattern;

/**
 * 防止克隆
 *
 * @author anthony
 * @datetime 2020/5/9 10:51
 */
public class SingletonByClone implements Cloneable {

    private static final SingletonByClone instance = new SingletonByClone();

    private SingletonByClone() {

    }

    public static SingletonByClone getInstance() {
        return instance;
    }


    /**
     * 防止克隆破坏
     *
     * @return
     * @throws CloneNotSupportedException
     */
    @Override
    protected Object clone() throws CloneNotSupportedException {
        // 下面这一行代码,虽然重写了 clone 方法,但是实际上调用 Object 的 clone 方法,相当于没有重写
        // 用于验证 clone 确实破坏了单例
        // return super.clone(); 
        
        // 下面返回是防止 clone 破坏单例模式
        return instance;
    }
}

测试类

package top.simba1949.singleton.pattern;

import cn.hutool.core.collection.ConcurrentHashSet;
import cn.hutool.core.lang.Assert;
import lombok.extern.slf4j.Slf4j;
import top.simba1949.singleton.pattern.impl.*;

import java.io.*;
import java.nio.file.Files;
import java.nio.file.Paths;
import java.util.concurrent.CountDownLatch;

/**
 * @author anthony
 * @datetime 2023/7/14
 */
@Slf4j
public class SingletonPatternApplication {

    public static void main(String[] args) throws Exception {
        // 饿汉式——静态常量
        // hungrySingletonByStaticConstant();

        // 饿汉式——静态代码块
        // hungrySingletonByStaticCodeBlock();

        // 懒汉式——普通懒汉式
        // lazySingletonByCommon();

        // 懒汉式——同步方法
        // lazySingletonBySyncMethod();

        // 懒汉式——同步代码块
        // lazySingletonBySyncCode();

        // 双重检测锁
        // singletonByDoubleCheckSync();

        // 静态内部类
        // singletonByStaticInnerClass();

        // 枚举单例
        // singletonByEnum();

        // 防止反射破坏单例
        // singletonByReflect();

        // 防止反序列化破坏单例
        // singletonBySerializable();

        // 防止 clone 破坏单例
        singletonByClone();
    }

    /**
     * 饿汉式——静态常量
     *
     * @throws Exception
     */
    public static void hungrySingletonByStaticConstant() throws Exception {
        ConcurrentHashSet<HungrySingletonByStaticConstant> container = new ConcurrentHashSet<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);

        // 连续获取对象,如果相等说明是同一个对象
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                public void run() {
                    HungrySingletonByStaticConstant instance = HungrySingletonByStaticConstant.getInstance();
                    log.info("获取实例的hashCode值为:{}", instance.hashCode());
                    container.add(instance);
                    countDownLatch.countDown();
                }
            }).start();
        }

        // 等待所有线程执行完成
        countDownLatch.await();

        Assert.isTrue(1 == container.size(), "容器内对象不是同一个对象");
    }

    /**
     * 饿汉式——静态代码块
     *
     * @throws Exception
     */
    public static void hungrySingletonByStaticCodeBlock() throws Exception {
        ConcurrentHashSet<HungrySingletonByStaticCodeBlock> container = new ConcurrentHashSet<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);

        // 连续获取对象,如果相等说明是同一个对象
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                public void run() {
                    HungrySingletonByStaticCodeBlock instance = HungrySingletonByStaticCodeBlock.getInstance();
                    log.info("获取实例的hashCode值为:{}", instance.hashCode());
                    container.add(instance);
                    countDownLatch.countDown();
                }
            }).start();
        }

        // 等待所有线程执行完成
        countDownLatch.await();

        Assert.isTrue(1 == container.size(), "容器内对象不是同一个对象");
    }

    /**
     * 懒汉式——普通懒汉式
     * @throws Exception
     */
    public static void lazySingletonByCommon() throws Exception {
        ConcurrentHashSet<LazySingletonByCommon> container = new ConcurrentHashSet<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);

        // 连续获取对象,如果相等说明是同一个对象
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                public void run() {
                    LazySingletonByCommon instance = LazySingletonByCommon.getInstance();
                    log.info("获取实例的hashCode值为:{}", instance.hashCode());
                    container.add(instance);
                    countDownLatch.countDown();
                }
            }).start();
        }

        // 等待所有线程执行完成
        countDownLatch.await();

        Assert.isTrue(1 == container.size(), "容器内对象不是同一个对象");
    }

    /**
     * 懒汉式——同步方法
     * @throws Exception
     */
    public static void lazySingletonBySyncMethod() throws Exception {
        ConcurrentHashSet<LazySingletonBySyncMethod> container = new ConcurrentHashSet<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);

        // 连续获取对象,如果相等说明是同一个对象
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                public void run() {
                    LazySingletonBySyncMethod instance = LazySingletonBySyncMethod.getInstance();
                    log.info("获取实例的hashCode值为:{}", instance.hashCode());
                    container.add(instance);
                    countDownLatch.countDown();
                }
            }).start();
        }

        // 等待所有线程执行完成
        countDownLatch.await();

        Assert.isTrue(1 == container.size(), "容器内对象不是同一个对象");
    }

    /**
     * 懒汉式——同步代码块
     * @throws Exception
     */
    public static void lazySingletonBySyncCode() throws Exception {
        ConcurrentHashSet<LazySingletonBySyncCode> container = new ConcurrentHashSet<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);

        // 连续获取对象,如果相等说明是同一个对象
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                public void run() {
                    LazySingletonBySyncCode instance = LazySingletonBySyncCode.getInstance();
                    log.info("获取实例的hashCode值为:{}", instance.hashCode());
                    container.add(instance);
                    countDownLatch.countDown();
                }
            }).start();
        }

        // 等待所有线程执行完成
        countDownLatch.await();

        Assert.isTrue(1 == container.size(), "容器内对象不是同一个对象");
    }

    /**
     * 双重检查
     * @throws Exception
     */
    public static void SingletonByDoubleCheckSync() throws Exception {
        ConcurrentHashSet<SingletonByDoubleCheckSync> container = new ConcurrentHashSet<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);

        // 连续获取对象,如果相等说明是同一个对象
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                public void run() {
                    SingletonByDoubleCheckSync instance = SingletonByDoubleCheckSync.getInstance();
                    log.info("获取实例的hashCode值为:{}", instance.hashCode());
                    container.add(instance);
                    countDownLatch.countDown();
                }
            }).start();
        }

        // 等待所有线程执行完成
        countDownLatch.await();

        Assert.isTrue(1 == container.size(), "容器内对象不是同一个对象");
    }

    /**
     * 静态内部类
     * @throws Exception
     */
    public static void singletonByStaticInnerClass() throws Exception {
        ConcurrentHashSet<SingletonByStaticInnerClass> container = new ConcurrentHashSet<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);

        // 连续获取对象,如果相等说明是同一个对象
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                public void run() {
                    SingletonByStaticInnerClass instance = SingletonByStaticInnerClass.getInstance();
                    log.info("获取实例的hashCode值为:{}", instance.hashCode());
                    container.add(instance);
                    countDownLatch.countDown();
                }
            }).start();
        }

        // 等待所有线程执行完成
        countDownLatch.await();

        Assert.isTrue(1 == container.size(), "容器内对象不是同一个对象");
    }

    /**
     * 枚举单例
     * @throws Exception
     */
    public static void singletonByEnum() throws Exception {
        ConcurrentHashSet<SingletonByEnum> container = new ConcurrentHashSet<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);

        // 连续获取对象,如果相等说明是同一个对象
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                public void run() {
                    SingletonByEnum instance = SingletonByEnum.INSTANCE;
                    log.info("获取实例的hashCode值为:{}", instance.hashCode());
                    container.add(instance);
                    countDownLatch.countDown();
                }
            }).start();
        }

        // 等待所有线程执行完成
        countDownLatch.await();

        Assert.isTrue(1 == container.size(), "容器内对象不是同一个对象");
    }

    /**
     * 通过反射获取实例
     * @throws Exception
     */
    public static void singletonByReflect() throws Exception {
        ConcurrentHashSet<SingletonByReflect> container = new ConcurrentHashSet<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);

        // 连续获取对象,如果相等说明是同一个对象
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                public void run() {
                    try {
                        // 通过反射获取实例
                        SingletonByReflect instance = SingletonByReflect.class.newInstance();
                        log.info("获取实例的hashCode值为:{}", instance.hashCode());
                        container.add(instance);
                    } catch (Exception e) {
                        log.info("获取到异常,异常信息为{}", e.getMessage(), e);
                    }
                    countDownLatch.countDown();
                }
            }).start();
        }

        // 等待所有线程执行完成
        countDownLatch.await();
        // 这里因为代码中是饿汉式(静态常量)模式,实例已经存在了,在通过反序列化后会一直报异常
        Assert.isTrue(1 >= container.size(), "容器内对象不是同一个对象");
    }

    /**
     * 通过反射获取实例
     * @throws Exception
     */
    public static void singletonBySerializable() throws Exception {
        String path = "d:\\SingletonBySerializable.txt";

        // 获取对象
        SingletonBySerializable instance4Serializable = SingletonBySerializable.getInstance();
        // 先将对象进行序列化
        ObjectOutputStream oos = new ObjectOutputStream(Files.newOutputStream(new File(path).toPath()));
        oos.writeObject(instance4Serializable);


        ConcurrentHashSet<SingletonBySerializable> container = new ConcurrentHashSet<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);

        // 连续获取对象,如果相等说明是同一个对象
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                public void run() {
                    try {
                        // 通过反序列化获取对象
                        ObjectInputStream ois = new ObjectInputStream(Files.newInputStream(Paths.get(path)));
                        SingletonBySerializable instance = (SingletonBySerializable)ois.readObject();
                        log.info("获取实例的hashCode值为:{}", instance.hashCode());
                        container.add(instance);
                    } catch (Exception e) {
                        log.info("获取到异常,异常信息为{}", e.getMessage(), e);
                    }
                    countDownLatch.countDown();
                }
            }).start();
        }

        // 等待所有线程执行完成
        countDownLatch.await();
        // 这里因为代码中是饿汉式(静态常量)模式,实例已经存在了,在通过反序列化后会一直报异常
        Assert.isTrue(1 >= container.size(), "容器内对象不是同一个对象");
    }

    /**
     * 通过反射获取实例
     * @throws Exception
     */
    public static void singletonByClone() throws Exception {
        // 获取对象
        SingletonByClone instance4Original = SingletonByClone.getInstance();

        ConcurrentHashSet<SingletonByClone> container = new ConcurrentHashSet<>();
        CountDownLatch countDownLatch = new CountDownLatch(1000);

        // 连续获取对象,如果相等说明是同一个对象
        for (int i = 0; i < 1000; i++) {
            new Thread(new Runnable() {
                public void run() {
                    try {
                        // 通过反序列化获取对象
                        SingletonByClone clone = (SingletonByClone) instance4Original.clone();
                        log.info("获取实例的hashCode值为:{}", clone.hashCode());
                        container.add(clone);
                    } catch (Exception e) {
                        log.info("获取到异常,异常信息为{}", e.getMessage(), e);
                    }
                    countDownLatch.countDown();
                }
            }).start();
        }

        // 等待所有线程执行完成
        countDownLatch.await();
        // 这里因为代码中是饿汉式(静态常量)模式,实例已经存在了,在通过反序列化后会一直报异常
        Assert.isTrue(1 >= container.size(), "容器内对象不是同一个对象");
    }
}

单例模式在 JDK 的应用

Runtime是java application与系统的接口, 可以启动单独的进程执行外部命令;监控方法甚至指令的执行;加载动态库(依赖于ClassLoader);可以报告、控制java app的状态(内存使用、暂停、退出等);

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

    public static Runtime getRuntime() {
        return currentRuntime;
    }
    
    private Runtime() {}
}

Tags:

最近发表
标签列表