深入探讨5种单例模式

文章目录

一、对比总览

以下是不同单例模式实现方式的特性对比表格。表格从线程安全性、延迟加载、实现复杂度、反序列化安全性、防反射攻击性等多个方面进行考量。

特性 饿汉式 饱汉式 饱汉式-双检锁 静态内部类 枚举单例(推荐)
线程安全性 ×
延迟加载 × ×
实现复杂度 ×
反序列化安全性 × × × ×
防反射攻击性 × × × ×

详细解释

  1. 线程安全性

    • 饿汉式:类加载时即创建实例(因为instance是static),线程安全。
    • 饱汉式:未使用同步机制,线程不安全。
    • 饱汉式-双检锁:使用同步块和双重检查锁,线程安全。
    • 静态内部类:通过类加载机制,线程安全。
    • 枚举单例:JVM确保线程安全。
  2. 延迟加载

    • 饿汉式:类加载时即创建实例,不具备延迟加载。
    • 饱汉式:实例在首次使用时创建,具备延迟加载。
    • 饱汉式-双检锁:实例在首次使用时创建,具备延迟加载。
    • 静态内部类:实例在首次使用时创建(因为静态内部类只有在使用时才会加载),具备延迟加载。
    • 枚举单例:类加载时即创建实例,不具备延迟加载。
  3. 实现复杂度

    • 饿汉式:实现简单。
    • 饱汉式:实现简单。
    • 饱汉式-双检锁:实现相对复杂。
    • 静态内部类:实现简单。
    • 枚举单例:实现简单。
  4. 反序列化安全性

    • 饿汉式饱汉式饱汉式-双检锁静态内部类 :需要实现 readResolve 方法以防止反序列化创建新实例。
    • 枚举单例:天然防止反序列化创建新实例,JVM保证。
  5. 防反射攻击性

    • 饿汉式饱汉式饱汉式-双检锁静态内部类:可能通过反射创建新实例。
    • 枚举单例 :防止反射攻击,创建新实例时抛出 IllegalArgumentException

二、代码

1. 饿汉式

java 复制代码
public class OrderManager1 {
    @Getter
    @Setter
    private Map<String, TradeOrder> orders = new HashMap<>();

    @Getter
    private static final OrderManager1 instance = new OrderManager1();

    /**
     * 添加订单
     *
     * @param order 顺序
     */
    public void addOrder(TradeOrder order) {
        orders.put(order.getId(), order);
    }

    /**
     * 获取订单
     *
     * @param orderId 订单id
     * @return {@link TradeOrder}
     */
    public TradeOrder getOrder(String orderId) {
        return orders.get(orderId);
    }
}

2. 饱汉式

java 复制代码
public class OrderManager2 {
    @Getter
    @Setter
    private Map<String, TradeOrder> orders = new HashMap<>();

    private static OrderManager2 instance;

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

    /**
     * 添加订单
     *
     * @param order 顺序
     */
    public void addOrder(TradeOrder order) {
        orders.put(order.getId(), order);
    }

    /**
     * 获取订单
     *
     * @param orderId 订单id
     * @return {@link TradeOrder}
     */
    public TradeOrder getOrder(String orderId) {
        return orders.get(orderId);
    }
}

3. 饱汉式-双检锁

java 复制代码
public class OrderManager3 {
    @Getter
    @Setter
    private Map<String, TradeOrder> orders = new HashMap<>();

    private static OrderManager3 instance;

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

    /**
     * 添加订单
     *
     * @param order 顺序
     */
    public void addOrder(TradeOrder order) {
        orders.put(order.getId(), order);
    }

    /**
     * 获取订单
     *
     * @param orderId 订单id
     * @return {@link TradeOrder}
     */
    public TradeOrder getOrder(String orderId) {
        return orders.get(orderId);
    }
}

4. 静态内部类

java 复制代码
public class OrderManager4 {
    @Getter
    @Setter
    private Map<String, TradeOrder> orders = new HashMap<>();

    private static class SingletonHelper{
        private static final OrderManager4 INSTANCE = new OrderManager4();
    }

    public static OrderManager4 getInstance(){
        return SingletonHelper.INSTANCE;
    }


    /**
     * 添加订单
     *
     * @param order 顺序
     */
    public void addOrder(TradeOrder order) {
        orders.put(order.getId(), order);
    }

    /**
     * 获取订单
     *
     * @param orderId 订单id
     * @return {@link TradeOrder}
     */
    public TradeOrder getOrder(String orderId) {
        return orders.get(orderId);
    }
}

5. 枚举单例

java 复制代码
public enum OrderManager5 {
    INSTANCE;

    @Getter
    @Setter
    private Map<String, TradeOrder> orders = new HashMap<>();

    /**
     * 添加订单
     *
     * @param order 顺序
     */
    public void addOrder(TradeOrder order) {
        orders.put(order.getId(), order);
    }

    /**
     * 获取订单
     *
     * @param orderId 订单id
     * @return {@link TradeOrder}
     */
    public TradeOrder getOrder(String orderId) {
        return orders.get(orderId);
    }
}

三、性能对比

java 复制代码
package org.dragon.singleton;

import lombok.extern.slf4j.Slf4j;

import java.util.*;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
import java.util.function.Supplier;

@Slf4j
public class SingletonPerformanceTest {
    static long timeout = 20; // 超时时间,单位为秒
    static int testIterations = 10_000_000; // 测试次数
    static int threadCount = 1000; // 并发线程数
    static Map<String, HashMap<String, Long>> result = new HashMap<>();

    public static void main(String[] args) {
        /*
        * 多次调用,结果是最后一次调用存入。为什么多次调用,因为单次test不准确,总是靠前的OrderManager跑的快,可能是因为Java某些机制导致的
        * 所以多次调用,逐渐平稳。
        * */
        firstCreationTest();
        mulAccessTest();
        mulAccessTest();
        mulAccessTest();
        ConcurrentAccessTest();
        ConcurrentAccessTest();
        printRes();
        ConcurrentAccessTest();
        printRes();
        ConcurrentAccessTest();
        printRes();
    }

    /**
     * 打印结果
     */
    private static void printRes(){
        ArrayList<String> names = new ArrayList<>();
        names.add(OrderManager1.class.getSimpleName());
        names.add(OrderManager2.class.getSimpleName());
        names.add(OrderManager3.class.getSimpleName());
        names.add(OrderManager4.class.getSimpleName());
        names.add(OrderManager5.class.getSimpleName());
        // 表头
        System.out.printf("%-20s%-20s%-25s%-25s%-20s%n", "Singleton Type", "First Creation (ms)", "Multiple Access (ms)", "Concurrent Access (ms)", "Memory Used (MB)");
        System.out.println("---------------------------------------------------------------------------------------------------------------");

        for (String name : names) {
            // 打印结果,转换时间为毫秒
            System.out.printf("%-20s%-20.3f%-25.3f%-25.3f%-20.3f%n", name, result.get(name).get("firstCreation") / 1_000_000.0, result.get(name).get("mulAccess") / 1_000_000.0, result.get(name).get("ConcurrentAccess") / 1_000_000.0, 0 / (1024.0 * 1024.0));
        }
    }

    /**
     * 首次创建测试
     */
    private static void firstCreationTest(){
        List<Runnable> tests = new ArrayList<>();
        tests.add(()->firstCreation(OrderManager1::getInstance));
        tests.add(()->firstCreation(OrderManager2::getInstance));
        tests.add(()->firstCreation(OrderManager3::getInstance));
        tests.add(()->firstCreation(OrderManager4::getInstance));
        tests.add(()->firstCreation(() -> OrderManager5.INSTANCE));
        // 随机化测试顺序
        Collections.shuffle(tests);
        //run
        for (Runnable test : tests) {
            test.run();
            log.info("Complete one test");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 多次访问测试
     */
    private static void mulAccessTest(){
        List<Runnable> tests = new ArrayList<>();
        tests.add(()->mulAccess(OrderManager1::getInstance, testIterations));
        tests.add(()->mulAccess(OrderManager2::getInstance, testIterations));
        tests.add(()->mulAccess(OrderManager3::getInstance, testIterations));
        tests.add(()->mulAccess(OrderManager4::getInstance, testIterations));
        tests.add(()->mulAccess(() -> OrderManager5.INSTANCE, testIterations));
        // 随机化测试顺序
        Collections.shuffle(tests);
        //run
        for (Runnable test : tests) {
            test.run();
            log.info("Complete one test");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 多线程访问测试
     */
    private static void ConcurrentAccessTest(){
        List<Runnable> tests = new ArrayList<>();
        tests.add(()->ConcurrentAccess(OrderManager1::getInstance, testIterations, threadCount));
        tests.add(()->ConcurrentAccess(OrderManager2::getInstance, testIterations, threadCount));
        tests.add(()->ConcurrentAccess(OrderManager3::getInstance, testIterations, threadCount));
        tests.add(()->ConcurrentAccess(OrderManager4::getInstance, testIterations, threadCount));
        tests.add(()->ConcurrentAccess(() -> OrderManager5.INSTANCE, testIterations, threadCount));
        // 随机化测试顺序
        Collections.shuffle(tests);
        //run
        for (Runnable test : tests) {
            test.run();
            log.info("Complete one test");
            try {
                Thread.sleep(200);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * 首次创建
     *
     * @param singletonSupplier 单一供应商
     * @return long ns
     */
    private static <T> long firstCreation(Supplier<T> singletonSupplier){
        // 测试首次创建时间
        long startTime = System.nanoTime();
        T instance = singletonSupplier.get();
        long endTime = System.nanoTime();
        long resTime = endTime - startTime;
        //save res
        String simpleName = instance.getClass().getSimpleName();
        HashMap<String, Long> resMap = result.computeIfAbsent(simpleName, k->new HashMap<>());
        resMap.put("firstCreation", resTime);
        return resTime;
    }

    /**
     * 多次访问
     *
     * @param singletonSupplier 单一供应商
     * @param iterations        迭代
     * @return long ns
     */
    private static <T> long mulAccess(Supplier<T> singletonSupplier, int iterations){
        //预热
        for (int i = 0; i < 100_000; i++) {
            T instance = singletonSupplier.get();
        }
        //计算
        long startTime = System.nanoTime();
        for (int i = 0; i < iterations; i++) {
            T instance = singletonSupplier.get();
        }
        long endTime = System.nanoTime();
        long resTime = endTime - startTime;
        //save res
        String simpleName = singletonSupplier.get().getClass().getSimpleName();
        HashMap<String, Long> resMap = result.computeIfAbsent(simpleName, k->new HashMap<>());
        resMap.put("mulAccess", resTime);
        return resTime;
    }

    /**
     * 并发访问
     *
     * @param singletonSupplier 单一供应商
     * @param iterations        迭代
     * @param threadCount       线程数
     * @return long ns
     */
    private static <T> long ConcurrentAccess(Supplier<T> singletonSupplier, int iterations, int threadCount){
        ExecutorService executorService = Executors.newFixedThreadPool(100);
        //预热
        CountDownLatch latch1 = new CountDownLatch(100);
        for (int i = 0; i < threadCount; i++) {
            executorService.submit(() -> {
                for (int j = 0; j < 100_000; j++) {
                    T instance = singletonSupplier.get();
                }
                latch1.countDown();
            });
        }
        try {
            boolean completed = latch1.await(timeout, TimeUnit.SECONDS);
            if (!completed) {
                System.out.println("Concurrent access test for 预热" + singletonSupplier.get().getClass().getSimpleName() + " timed out!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        //计算
        CountDownLatch latch2 = new CountDownLatch(threadCount);
        long startTime = System.nanoTime();
        for (int i = 0; i < threadCount; i++) {
            executorService.submit(() -> {
                for (int j = 0; j < iterations; j++) {
                    T instance = singletonSupplier.get();
                }
                latch2.countDown();
            });
        }
        try {
            boolean completed = latch2.await(timeout, TimeUnit.SECONDS);
            if (!completed) {
                System.out.println("Concurrent access test for " + singletonSupplier.getClass().getSimpleName() + " timed out!");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        long endTime = System.nanoTime();
        long concurrentAccessTime = endTime - startTime;

        executorService.shutdown();
        //save res
        String simpleName = singletonSupplier.get().getClass().getSimpleName();
        HashMap<String, Long> resMap = result.computeIfAbsent(simpleName, k->new HashMap<>());
        resMap.put("ConcurrentAccess", concurrentAccessTime);
        return concurrentAccessTime;
    }
}

结果输出如下

txt 复制代码
[17:15:54.519] [INFO ] org.dragon.singleton.SingletonPerformanceTest 73 firstCreationTest - Complete one test
[17:15:54.730] [INFO ] org.dragon.singleton.SingletonPerformanceTest 73 firstCreationTest - Complete one test
[17:15:54.936] [INFO ] org.dragon.singleton.SingletonPerformanceTest 73 firstCreationTest - Complete one test
[17:15:55.141] [INFO ] org.dragon.singleton.SingletonPerformanceTest 73 firstCreationTest - Complete one test
[17:15:55.347] [INFO ] org.dragon.singleton.SingletonPerformanceTest 73 firstCreationTest - Complete one test
[17:15:55.554] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:55.782] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:56.007] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:56.227] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:56.445] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:56.669] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:56.906] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:57.146] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:57.376] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:57.598] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:57.818] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:58.054] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:58.276] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:58.495] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:58.716] [INFO ] org.dragon.singleton.SingletonPerformanceTest 97 mulAccessTest - Complete one test
[17:15:59.430] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:15:59.658] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:16:02.737] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:16:08.533] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:16:13.700] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:16:19.432] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:16:24.632] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:16:30.366] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:16:35.516] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:16:40.431] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
Singleton Type      First Creation (ms) Multiple Access (ms)     Concurrent Access (ms)   Memory Used (MB)    
---------------------------------------------------------------------------------------------------------------
OrderManager1       0.010               16.848                   4928.236                 0.000               
OrderManager2       0.010               18.592                   5504.781                 0.000               
OrderManager3       0.009               19.127                   5513.309                 0.000               
OrderManager4       0.241               18.772                   4920.940                 0.000               
OrderManager5       0.117               16.637                   4704.835                 0.000               
[17:16:45.002] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:16:48.982] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:16:52.778] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:16:57.834] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:17:02.298] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
Singleton Type      First Creation (ms) Multiple Access (ms)     Concurrent Access (ms)   Memory Used (MB)    
---------------------------------------------------------------------------------------------------------------
OrderManager1       0.010               16.848                   4217.333                 0.000               
OrderManager2       0.010               18.592                   4340.869                 0.000               
OrderManager3       0.009               19.127                   4824.581                 0.000               
OrderManager4       0.241               18.772                   3743.032                 0.000               
OrderManager5       0.117               16.637                   3558.995                 0.000               
[17:17:06.778] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:17:11.231] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:17:15.733] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:17:20.812] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
[17:17:25.837] [INFO ] org.dragon.singleton.SingletonPerformanceTest 121 ConcurrentAccessTest - Complete one test
Singleton Type      First Creation (ms) Multiple Access (ms)     Concurrent Access (ms)   Memory Used (MB)    
---------------------------------------------------------------------------------------------------------------
OrderManager1       0.010               16.848                   4281.649                 0.000               
OrderManager2       0.010               18.592                   4849.279                 0.000               
OrderManager3       0.009               19.127                   4782.224                 0.000               
OrderManager4       0.241               18.772                   4267.228                 0.000               
OrderManager5       0.117               16.637                   4233.907                 0.000               

进程已结束,退出代码为 0

可以去多跑几次,基本上最后一种枚举单例,性能属于最好的一批。并且也最安全。

相关推荐
神仙别闹3 分钟前
基于java的改良版超级玛丽小游戏
java
黄油饼卷咖喱鸡就味增汤拌孜然羊肉炒饭26 分钟前
SpringBoot如何实现缓存预热?
java·spring boot·spring·缓存·程序员
暮湫43 分钟前
泛型(2)
java
超爱吃士力架1 小时前
邀请逻辑
java·linux·后端
南宫生1 小时前
力扣-图论-17【算法学习day.67】
java·学习·算法·leetcode·图论
转码的小石1 小时前
12/21java基础
java
李小白661 小时前
Spring MVC(上)
java·spring·mvc
GoodStudyAndDayDayUp1 小时前
IDEA能够从mapper跳转到xml的插件
xml·java·intellij-idea
装不满的克莱因瓶2 小时前
【Redis经典面试题六】Redis的持久化机制是怎样的?
java·数据库·redis·持久化·aof·rdb
n北斗2 小时前
常用类晨考day15
java