实现一个线程安全的单例模式

单例模式

单例模式能保证某个类在程序中只存在唯⼀⼀份实例,⽽不会创建出多个实例

某个类,在一个类,只应该创建出一个实例,使用单例模式,就可以对咱们的代码进行一个更严格的校验和检查

单例模式具体的实现⽅式有很多.最常⻅的是"饿汉"和"懒汉"两种
单例模式分为:饿汉模式懒汉模式。单例模式之所以被称为单例模式,是因为我们在创建单例模式类的时候,就把该类的构造方法使用private进行修饰,以便在该类外,不能直接创建出一个实例。

饿汉模式

class SingLeton{
    private static SingLeton instance = new SingLeton();
    public static SingLeton  getInstance() {
        return instance ;
    }
    private SingLeton(){}
}

对于饿汉模式来说,getInstance直接返回Instance实例,这个操作本质就是"读操作"

多个线程读同一个变量,是线程安全的!!

懒汉模式

实质是在该模式中不着急初始化出实例,在类外需要的时候,在进行初始化

class SingletonLazy{
    private volatile static SingletonLazy instance = null;

    public static SingletonLazy getInstance() 
        if(instance == null ){
              instance = new SingletonLazy();
        }
        return instance;
    }

    public SingletonLazy() {}
}
public class ThreadDemo10 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
    }

}

 

针对上述问题,如何解决,是需要把if和new 两个操作,打包成一个原子的

代码修改如下

class SingletonLazy{
 
    private static Object locker = new Object();

    public  static  SingletonLazy getInstance() {
       
        synchronized (locker) {
            if(instance == null ){
                instance = new SingletonLazy();
            }
        }
        return instance;
    }
    public SingletonLazy() {}
}
public class ThreadDemo10 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);
    }

}

但是加了锁 ,又出现了新的问题

如果instance 已经创建过了  此后后续在调用getInstance 就直接返回instance实例了吧,此时,针对已经没有线程安全问题的代码,仍然是每次调用都先加锁在解锁,效率非常低了.

加锁意味着可能会产生阻塞,一旦线程阻塞,啥时候能解除,就不知道了

解决问题:在加锁外头套层if 判定一下这个代码是否需要加锁 ,如果需要加锁,才加,不需要加锁,那就算了

这时候 代码仍有点问题

指令重排序:也是编译器优化的一种方式

                    调整原有代码的执行顺序,保证逻辑不变的前提下,提高程序的效率

上述代码中,由于t1线程执行完1 3 之后 调度走,此时instance 指向的是一个非null的 但是未初始化的对象,此时t2线程判定instance == null 不成立 就会直接return  如果t2继续使用instance里面的属性和方法,就会出现问题(此时这里的属性 都是未初始化的"全0 "值) 就会引起代码逻辑上出现问题

解决上述问题 核心思路 还是volatile

volatile有两个功能

1.保证内存可见性:每次访问变量必须都要重新读取内存,而不会优化到寄存器/缓存中

2.禁止指令重排序.真对这个被volatile修饰的变量的读写操作相关指令,是不能被重新排序的

class SingletonLazy{
    private volatile static SingletonLazy instance = null;
    private static Object locker = new Object();

    public  static  SingletonLazy getInstance() {
        if(instance == null) {
            synchronized (locker) {
                if(instance == null ){
                    instance = new SingletonLazy();
                }
             }
        }
        return instance;
    }
    public SingletonLazy() {}
}
public class ThreadDemo10 {
    public static void main(String[] args) {
        SingletonLazy s1 = SingletonLazy.getInstance();
        SingletonLazy s2 = SingletonLazy.getInstance();
        System.out.println(s1 == s2);
    }

}

总结一下 线程安全的单例模式 ---懒汉模式

1.在正确位置加锁

2.双重if 判定

3.使用volatile关键字

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值