设计模式学习笔记(五)——单例模式

有一些对象我们只需要使用一个,例如线程池,缓存,对话框,日志对象等等。如果这些对象有多个实例,就会导致许多问题产生,像程序的行为异常,内存溢出,或者是不一致的结果。
使用全局变量可以达到效果,但同时,全局变量在程序一开始就被创建,如果这是一个占资源大的对象,而在这次执行中程序没有使用到该变量,这就形成了资源浪费。

定义: 单例模式确保类只有一个实例,并提供一个全局访问点。

经典的单例模式实现

public class Singleton {

    private static Singleton uniqueInstance;

    private Singleton(){}

    public static Singleton getInstance(){
        if (uniqueInstance == null){
            uniqueInstance = new Singleton();
        }
        return uniqueInstance;
    }
    //其他方法
}

上面就是一个经典的单例模式,私有构造器,共有创建方法,当类变量为空时,我们创建一个实例并返回它,我们称这种方式为延迟实例化,也就是”饿汉模式“。下面再看一个例子

原始代码

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

为了使得不出现不一致的情况,我们决定使用单例模式,如下
懒汉模式

public class ChocolateBoiler {

    private boolean empty;
    private boolean boiled;
    private static ChocolateBoiler instance;

    private ChocolateBoiler() {
        empty = true;
        boiled = false;
    }

    public static ChocolateBoiler getInstance(){
        if(instance == null){
            instance = new ChocolateBoiler();
        }
        return instance;
    }
    //其他方法
}

出现问题了: 我们的fill()方法竟然允许在boil()时候继续添加原料,这不符合我们的逻辑。
经过检查,我们发现这是由于有两个线程同时去获取这个实例造成的错误,解决方法如下

加锁

public class ChocolateBoiler {

    private boolean empty;
    private boolean boiled;
    private static ChocolateBoiler instance;

    private ChocolateBoiler() {
        empty = true;
        boiled = false;
    }

    public static synchronized ChocolateBoiler getInstance(){
        if(instance == null){
            instance = new ChocolateBoiler();
        }
        return instance;
    }
    //其他方法
}

在方法上加锁能确保同时只有一个线程能够拿到该实例。但是,使用synchronized会降低性能,而我们只有第一次执行该方法时才需要同步,之后每次调用该方法都会造成对性能的浪费。

“饿汉模式”

public class ChocolateBoiler {

    private boolean empty;
    private boolean boiled;
    private static ChocolateBoiler instance = new ChocolateBoiler();

    private ChocolateBoiler() {
        empty = true;
        boiled = false;
    }

    public static synchronized ChocolateBoiler getInstance(){
        return instance;
    }
}

双重加锁检查

public class ChocolateBoiler {

    private boolean empty;
    private boolean boiled;
    private volatile static ChocolateBoiler instance;

    private ChocolateBoiler() {
        empty = true;
        boiled = false;
    }

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

上例使用了双重检查,首先检查是否创建了实例,只有未创建的时候才会进入同步块。这样就大大降低了性能消耗

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值