• Head First设计模式(阅读笔记)-05.单例模式


    巧克力工厂

    巧克力工厂需要将牛奶和巧克力混合,因此需要一个巧克力锅炉,具体代码如下:


    public class ChocolateBoiler{
        private boolean empty;  // 判断是否为空
        private boolean boiled;  // 判断是否煮沸
        public ChocolateBoiler(){  // 刚开始时锅炉是空的
            empty = true;  
            boiled = false;
        }
        public void fill(){
            // 锅炉为空时
            if(isEmpty()){
                empty = false;
                boiled = false;
                // 开始填充牛奶和巧克力
            }
        }
        public void drain(){
            if(!isEmpty() && isBoiled()){  // 如果锅炉满了而且已煮过
                // 排除煮沸的巧克力和牛奶
                empty = true;  // 锅炉重新变为空
            }
        }
        public void boil(){
            if(!isEmpty() && !isBoiled()){  // 如果锅炉满了而且未煮过
                // 将混合物煮沸
                boiled = true;  // 已经煮沸了
            }
        }
        public boolean isEmpty(){
            return empty;
        }
        public boolean isBoiled(){
            return boiled;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34

    单例模式

    确保一个类只有一个实例,并且提供一个全局访问点


    经典单例实现

    因为只允许一个巧克力锅炉存在,所以自然就想到使用单例模式

    这种方式十分简单,但是存在线程不安全的问题(图中出现obj1obj2)


    public class ChocolateBoiler{
        private boolean empty;
        private boolean boiled;
        private static ChocolateBoiler uniqueInstance;  // ① 静态变量记录ChocolateBoiler类的唯一实例
        public static ChocolateBoiler getInstance(){  // ② 提供一个公共方法让外部可以获取到该唯一实例
            if(uniqueInstance == null){
                uniqueInstance = new ChocolateBoiler();
            }
            return uniqueInstance;
        }
        private ChocolateBoiler(){  // ③ 将构造方法改为私有,只有ChocolateBoiler类内部才能调用
            empty = true;  
            boiled = false;
        }
        // 其他代码一致
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    在这里插入图片描述

    处理多线程

    既然存在多线程问题,不如将getInstance方法改为同步方法

    这样虽然可以解决线程安全问题,但是性能实在过于低下


    public class ChocolateBoiler{
        // 加上synchronized关键字
        public static synchronized ChocolateBoiler getInstance(){  
            if(uniqueInstance == null){
                uniqueInstance = new ChocolateBoiler();
            }
            return uniqueInstance;
        }
        // 其他代码一致
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    不采用延迟实例化

    前两种方式都采用了延迟实例化,如果程序总是创建并使用该单例对象,或者创建和运行该单例对象时负担不重,可以采用静态初始化器创建单例对象

    之所以能使用该方法是因为JVM可以保证任何线程访问uniqueInstance静态变量前一定会创建该单例对象


    public class ChocolateBoiler{
        // 在静态初始化器创建单例对象
        private static ChocolateBoiler uniqueInstance = new ChocolateBoiler(); 
        public static ChocolateBoiler getInstance(){  
            return uniqueInstance;
        }
        // 其他代码一致
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    使用双重检查锁

    先检查是否创建了实例,如果未创建再进行同步,该方法的是否必须保证再Java5以上的版本


    public class ChocolateBoiler{
        // 在静态初始化器创建单例对象
        private volatile static ChocolateBoiler uniqueInstance;  // 这里需要使用volatile关键字
        public static ChocolateBoiler getInstance(){  
            if(uniqueInstance = null){
                synchronized(ChocolateBoiler.class){
                    if(uniqueInstance = null){
    					uniqueInstance = new ChocolateBoiler();
                    }
                }
    
            }
            return uniqueInstance;
        }
        // 其他代码一致
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16

    参考

    Head First 设计模式-单例模式

    设计模式-单例模式

    java 单例模式中双重检查锁定 volatile 的作用?

  • 相关阅读:
    京东云开发者|探寻软件架构的本质,到底什么是架构?
    算法金 | 突破最强算法模型,决策树算法!!
    Hive的常用内置函数
    mysql 从入门到放弃— 数据库设计
    天线材质介绍--FPC天线
    【深度学习】PyTorch深度学习笔记02-线性模型
    微调文本到图像扩散模型新方法DreamBooth,实现主题驱动生成(CVPR 2023)
    交叉验证和网格验证的方法
    完成原型设计的五个步骤
    任萧文——少年何妨梦摘星?敢挽桑弓射玉衡
  • 原文地址:https://blog.csdn.net/qq_41398418/article/details/128061276