• 设计模式 | 单例模式


    1. 最简单的实现方式

    之前面试百度的时候,面试官问我:“单例模式需要注意的点是什么?”,当时感觉答得有点乱,
    经过之后的学习总结下来应该注意以下两点:

    1. 构造方法一定要用private修饰!不可以被new出来!
    2. 并发场景下也要保证它是一个对象!
    public class Singleton {
    
        private static Singleton INSTANCE;
    
        // 构造方法一定要用private修饰!
        private Singleton() {
        }
    
        public static Singleton getSingleton() {
        // 懒加载实现
            if (INSTANCE == null) {
                INSTANCE = new Singleton();
            }
            return INSTANCE;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    存在问题:

    1. 多线程环境下不适用!会出现被new多次的问题!

    2. DCL双重检查实现

    dcl —> duplication check lock

    public class Singleton {
    
        private volatile static Singleton INSTANCE;
    
        // 构造方法一定要用private修饰!
        private Singleton() {
        }
    
        public static Singleton getSingleton() {
            if (INSTANCE == null) {
                synchronized(Singleton.class){
                    if (INSTANCE == null) {
                        INSTANCE = new Singleton();
                    }
                }
            }
            return INSTANCE;
        }
    }
    
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    解释:

    1. 为什么使用volatile?

      防止指令重排,获取到半个对象

    2. 为什么要两次判断?

      防止特殊情况:俩对象同时进入到 if (INSTANCE == null) 这个判断中


    3. 静态内部类实现

    public class Singleton2 {
    
        // 构造方法一定要用private修饰!
        private Singleton2() {
        }
    
        private static class Singleton2Dummy {
            private static final Singleton2 INSTANCE = new Singleton2();
        }
    
        public static Singleton2 getSingleton() {
            return Singleton2Dummy.INSTANCE;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    说明:

    1. 这是线程安全的,为什么?

      静态内部类是静态的,所以是类的一部分,加载当前类(Singleton2 )的时候也会加载这个内部类,并给他的值初始化,相当于已经实例化好了这个内部类,不管你调用与否,它都在这里生成了,且是独一份的!

    缺点:
    2. 非要说缺点那就是他不管用不用都被实例化了,会占用点内存!


    4. 枚举类 (最优解)

    《Effective Java》作者是约书亚·布洛克(Joshua Bloch)在书中提到单例模式最优解如下:

    public enum Singleton3 {
    
        INSTANCE;
    
        public void 具体实现() {
        }
    
        public static void main(String[] args) {
            IntStream.range(1, 100).forEach(num -> {
                new Thread(() -> {
                    System.out.println(Singleton3.INSTANCE.hashCode());
                }).start();
            });
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15

    说明:

    1. 线程安全:是通过jvm层面的实现来保证线程安全的
    2. 枚举类没有构造方法
  • 相关阅读:
    2159. Order Two Columns Independently
    华为认证系统学习大纲及课程
    Python中的matplotlib与Pygal的安装、使用与实例
    《异常检测——从经典算法到深度学习》27 可执行且可解释的在线服务系统中重复故障定位方法
    Node 入门
    golang 协程的实现原理
    北斗导航 | 从事卫星导航工作需要知道的基本算法
    platform驱动模型再叙
    【C++】C++中那些有点意思,但一般也用不到的替代运算符
    为什么要用微前端?如何使用乾坤微前端?
  • 原文地址:https://blog.csdn.net/weixin_40597409/article/details/126645093