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