设计一个单例模式
类与类之间的关系
目标:对外只有一个对象
介绍double-checking单例模式
使用volatile进行锁定资源
饿汉式:直接实例化了对象
懒汉式:没有直接实例化对象
DCL单例设计模式实例demo
图示:
package thread.rearrangement;
/**
* DCL单例模式:
* 1、在多线程环境下,对外存在一个对象--->外部不能new对象--->构造器私有化,避免外部new构造器(懒汉式加入并发控制)
* 2、内部提供私有的静态属性--->存储对象的地址
* 3、对外部提供公共的静态方法--->获取属性(该属性存了对象的地址)
* @since JDK 1.8
* @date 2021/6/22
* @author Lucifer
*/
public class DoubleCheckedLocking {
/*提供私有的静态属性*/
private static volatile DoubleCheckedLocking instance;
//如果没有volatile其他线程可能访问到一个没有初始化的对象
/*构造器私有化*/
private DoubleCheckedLocking(){
}
/*提供公共的静态方法访问私有属性*/
public static DoubleCheckedLocking getInstance(){
/*如果已经存在对象,直接返回--->doublechecking避免不必要的同步(已经存在对象)*/
if (null!=instance){
/*直接返回结果*/
return instance;
}
/*加入同步块锁住类--->class对象*/
synchronized (DoubleCheckedLocking.class){
/*当没有对象的时候返回对象*/
if (null==instance){
/*创建一个对象*/
instance = new DoubleCheckedLocking();
/*
在这里考虑指令重排
在实例化一个对象的时候步骤:
1、开辟空间
2、初始化对象信息(对、块、栈)
3、返回对象地址给引用
如果构造器很慢,就有可能下面的内容先执行。
举例:
A还在初始化对象
B已经拿到了引用
就会形成空对象!!!
如何避免?
在属性前加入volatile保证可见性
*/
}
}
/*返回类对象*/
return instance;
}
public static void main(String[] args) {
Thread t = new Thread(()-> {
System.out.println(DoubleCheckedLocking.getInstance());
});
t.start();
if (t.equals(DoubleCheckedLocking.getInstance())){
System.out.println(DoubleCheckedLocking.getInstance());
}else {
System.out.println("Error!");
}
/*直接输出DoubleCheckedLocking.getInstance就不会出现指令重排*/
System.out.println(DoubleCheckedLocking.getInstance());
/*
这里也发生了指令重排的情况
因为Thread是实例化线程,DoubleCheckedLocking.getInstance是直接获取地址
所以先执行了下面的if判断
所以执行的结果是先打印处了Error后打印出了getInstance的地址
*/
}
}
/*
1、如果两个线程A、B进入线程
2、A进入创建对象但是耗时时间长
3、B进入时候A对象没有写回主存,导致B也创建了一个对象
这样就形不成单例设计模式了,所以要加入同步块
1、锁住类的.class对象(类的模子)
2、多个线程进入,就不会造成多线程创建多个对象
如果已经存在对象则不需要等待,所以需要doublechecking再次监测
*/