剑指offer 题2 单例模式

  1. 懒汉式–第一次调用时加载
    1. 第一版 上来没有想很多,直接敲起,使用synchronized保证线程安全,代码如下:

        public class  Single{
                private static Single instance;
                //将构造函数声明称私有的
                private Single(){};
                //使用synchronized保证安全
                public static synchronized Single getSingle(){
                    if(instance==null){
                        instance=new Single();
                    }
                    return instance;
                }
            }    
    

    2.第一版的缺点:当两个线程 同时访问线程锁的时候,出现竞争,一个获得锁,一个等待,等第一个线程执行完之后,第二个线程才能获得锁,执行判断条件.这样效率会很低.
    看了一篇博客后发现,可以使用双重判断进行优化,改进版如下:

    public  class Single2 {
    private static Single2 instance;
    // 将构造函数声明称私有的
    private Single2() {
    };
    private Single2 getSingle() {
        //第一重循环判断instance存在时跳过循环,直接返回
        if (instance == null) {
            //使用synchronized代码块保证,多线程同时访问时线程同步
            synchronized (Single.class) {
                //防止等待的线程进入创建实例
                if(instance==null){
                    instance=new Single2();
                }
            }
    
        }
        return instance;
    }
    }
    

    3.博客中提到的完美版本:volatile关键字版
    解决上面版本中可能出现的指令重排导致不稳定结果的出现:

    1.给instance分配内存
    2.调用构造函数初始化成员变量,创建实例
    3.将instance指向分配的内存空间//注意是指向内存空间,不是实例
    

       在JVM中可能会出现指令重排问题而导致步骤2和3的顺序不能保证,当线程1如果在执行完指令1后直接执行指向3,如果在其执行完3而未执行2时,线程2开始访问第一层if判断,注意这时的instance就已经不是null了,它已经指向了jvm分配的内存空间,这样线程2就会直接返回中间态instance,继续执行下去将会报错.
       解决方法使用volatile关键字修饰instance,volatile会产生内存屏障,从而禁止指令重排

    public class Single3 {
    //使用volatile關鍵字,禁止指令重排
    private static volatile Single3 instance;
    // 将构造函数声明成私有的
    private Single3() {
    };
    private Single3 getSingle() {
        //第一重循环判断instance存在时跳过循环,直接返回
        if (instance == null) {
            //使用synchronized代码块保证,多线程同时访问时线程同步
            synchronized (Single3.class) {
                //防止等待的线程进入创建实例
                if(instance==null){
                    instance=new Single3();
                }
            }
        }
        return instance;
    }
    }

2.饿汉式 –在类装载时创建实例
这种情况下实例在类加载器加载类初始化,这时jvm会保证线程同步

    public class Single4{
    private static final Single4 INSTANCE =new Single4();
    private Single4(){};
    public static Single4 geSingle(){
        return INSTANCE;
    }
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值