1.单例模式理解:是为了满足一些场景,一个对象只能创建一个实例对象的场景。
流程:
1.构造方法私有化,
2.声明对象 位静态
3.类方法返回实例化后的对象。
2.将单例模式分为两类 懒汉式和饿汉式:
懒汉式:特点在定义Singleton是就new
public class Singleton{
private static Singleton singleton=new Singleton();
private Singleton(){}
public static Singleton newInstance(){
return singleton;
}
}
优点:简单,而且保证了线程安全,不管有多少个线程访问都只返回一个Singleton实例化对象
饿汉式单例模式:
1.普通
public class Singleton{
private static Singleton singleton;
private Singleton(){}
public static Singleton newInstance(){
if(singleton==null){
singleton=new Singleton();
}
return singleton;
}
}
线程不安全:if判断确实可以保证只创建一个Singleton的实例对象。
但是在多线程并发场景下,由于没有加锁,多个线程可能都会进入到if中,那么这些线程就可以进行对象实例化操作产生多个对象。
if(singleton==null){
//多个线程进入id判断里面,
singleton=new Singleton();
}
双层检验锁单例模式:
public class Singleton{
private static volatile Singleton singleton;
private Singleton(){}
public static Singleton newInstance(){
if(singleton==null){
//1
synchronized(Singleton.class){
//2
if(singleton==null){
singleton=new Singleton();
}
}
}
return singleton;//3
}
}
按照上面的注释:来分析上面的代码:
1.在调用类方法newInstance的时候所有线程都可以进入//1中
2.多个线程中只有一个线程会拿到锁 进入同步代码块
3.拿到锁后要再次判断当前对象引用是否指向实例,因为在这个线程之前还会有别的线程拿到锁执行同步代码块。
到此流程基本结束:还有几个问题:
1.singleton为什么要用volatile修饰?
首先,synchronized 保证了只有一个线程对singleton进行实例操作,但是操作的对象是引用数据类型,换句话说该操作不是原子的。那么久可能发生指令重排。
正常情况:实例化对象的流程:
1.在堆上开辟空间
2.属性初始化
3.将栈上空间指向堆。
正常是1——>2——>3
但是如果没有volatile关键字就可能发生指令重排:
假设 1——>3——>2
如果没有属性的话没有问题,
一旦有了属性,假设现在有两个属性:
name=“hahaha”,
age=12;
由于3步骤先执行,singleton已经不为空了,
当有新的线程尝试获取Singleton实例,到了外层判断,直接返回。
导致的结果就是,有能属性还没有来的及初始化就直接返回了,或者初始化了name..
2.为什么要有双重if判断:
内部的if是保证只有一个Singleton实例化对象被创建。
外层的if实际上是为了保证性能:
当Singleton已经被创建了,不进入内部,直接返回。
可以把这种机制:当做一个限量发售的鞋,
把多线程当做在排队等待鞋的人。
当一个人进入后买了这双鞋 实际上已经没有这双鞋了
通常做法是在门口立一个牌子,鞋已经售空 人看到这个牌子就可以直接走了
相当于外部的i f)。
如果没有这个牌子,外面的人就不知道鞋已经卖完,还在不断排队进入鞋店,浪费资源