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;
    }
}

单例模式

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


经典单例实现

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

这种方式十分简单,但是存在线程不安全的问题(图中出现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;
    }
    // 其他代码一致
}

在这里插入图片描述

处理多线程

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

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


public class ChocolateBoiler{
    // 加上synchronized关键字
    public static synchronized ChocolateBoiler getInstance(){  
        if(uniqueInstance == null){
            uniqueInstance = new ChocolateBoiler();
        }
        return uniqueInstance;
    }
    // 其他代码一致
}
不采用延迟实例化

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

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


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

先检查是否创建了实例,如果未创建再进行同步,该方法的是否必须保证再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;
    }
    // 其他代码一致
}

参考

Head First 设计模式-单例模式

设计模式-单例模式

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

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值