单例模式
单例模式能保证某个类在程序中只存在唯⼀⼀份实例,⽽不会创建出多个实例
某个类,在一个类,只应该创建出一个实例,使用单例模式,就可以对咱们的代码进行一个更严格的校验和检查
单例模式具体的实现⽅式有很多.最常⻅的是"饿汉"和"懒汉"两种
单例模式分为:饿汉模式和懒汉模式。单例模式之所以被称为单例模式,是因为我们在创建单例模式类的时候,就把该类的构造方法使用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关键字