单例模式(饿汉模式、懒汉模式、指令重排序)

目录

单例模式

饿汉模式

懒汉模式

指令重排序

单例模式的最终版本


单例模式代码为面试经典、高频问题!!

单例模式

使某个类,只有唯一一个对象.通过编码技巧,让编译器强行检查

饿汉模式

(类加载的时候就创建对象)

public class Singleton {
    //类加载的时候就创建
    private static Singleton instance=new Singleton();
    //后续需要获取这个类的实例,都通过getInstance方法来获取
    public static Singleton getInstance(){
        return instance;
    }
    //把构造方法设为私有,外面的代码就无法new出这个类的对象了
    private Singleton(){};
}
懒汉模式

(在第一次使用的时候创建对象)

public class SingleletonLazy {
    private static SingleletonLazy instance=null;
    public static SingleletonLazy getInstance(){
        if(instance==null){
            instance=new SingleletonLazy();
        }
        return instance;
    }
    private SingleletonLazy(){}
}

饿汉模式中,只是读取对象,不会有线程安全问题

懒汉模式中,又读又写对象,会有线程安全问题

如果两个线程中穿插执行了,就会new出多个实例

解决方式:

加锁 将if和new合并成一个整体

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

实际上加锁是一个开销很大的操作,可能会涉及锁冲突,一冲突就会涉及到阻塞等待

该如何解决呢?

在加锁语句外层,加一个判断条件,判定一下这里是否需要加锁

如果对象已经有了,线程就安全了,此时可以不加锁了

如果对象还没有,存在线程不安全的风险,就需要加锁

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

指令重排序

编译器为了执行效率,可能会调整执行顺序,前提是保持逻辑不变

单线程中无所谓,而多线程中可能会出现误判

上文中懒汉模式的代码:

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

new操作,可能出触发指令重排序

new操作分为三步:

①申请内存空间

②在内存空间上构造对象(构造方法)

③把内存的地址,赋值给instance引用

(会按照①②③、①③②的顺序来执行)

假如按照①③②,当t1执行完①③后,instance已经是一个非空的对象了,但是此时指向的是一个没有初始化的非法对象

而如果此时t2开始执行了,判断instance==null不成立,直接return instance,进一步t2线程的代码就可能会访问instance的属性和方法了

上述问题,解决方法——volatile

让volatile修饰instance,在instance修改过程中就不会出现指令重排序现象了

所以,我们得出

单例模式的最终版本

(还有反射、序列化、反序列化的问题,这里不做过多讨论)

注意①②③注意点

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值