单例模式: 一个类只创建一个对象,构造方法私有
1.饿汉模式
加载字节码文件时就创建对象
public class Singleton1 {
// 1.私有化构造方法
private Singleton1(){}
// 写一个私有的Singleton1类型的成员变量
private static final Singleton1 INSTANCE = new Singleton1();
// 2. 对外提供公共的静态方法,返回值就是A的唯一实例
public static Singleton1 getInstance(){
return INSTANCE;
}
}
2.懒汉模式
初始化第一个对象实例的时候创建对象
public class Singleton2 {
// 1.私有化构造方法
private Singleton2() {}
// 写一个私有的Singleton1类型的成员变量
private volatile static Singleton2 instance = null; //volatile 关键字禁止重排序
// 2. 对外提供公共的静态方法,返回值就是A的唯一实例
// t1 t2
public static Singleton2 getInstance() {
// 3. 判断是否是第一次获取/使用该实例
// t1 t2 t2发现instance不为null,跳过这段代码,直接返回
if (instance == null) { //此处判断目的:保证效率。只让实例化instance之前线程安全即可。
// t1
synchronized (Singleton2.class) { //加锁的目的:线程安全。是为了保证在多线程环境下,下属代码同时只有一个线程在操作
/**
* 下列三行代码可能会出现线程安全问题,但是只有再instance没有被实例化之前才会出现
* 上述加锁,太武断了
*/
// t1 -l 执行完指令1 和3 之后失去了CPU的执行权,instance不再为null,但是instance对象不完整。
if (instance == null) { // 此处判断的目的:保证instance只被创建一次
// t1 -l
instance = new Singleton2();
}
}
}
// t2返回的就是不完整的instance,使用时会出现问题。
return instance;
}
}
/*
* Double Check Lock 双重校验加锁
* 只有要求线程安全的同时,并且保证较高的效率,都可以使用DCL
*
*
* 指令重排
* jvm、cpu在执行代码期间,为了提高执行效率,会把某些指令乱序执行。
*
* user = new User();
* 上述代码执行步骤如下:
* 1. 开辟内存空间,生成空间首地址值 new
* 2. 初始化内存空间的内容 User(xxx)
* 3. 把上述空间的首地址值,赋值给user
*
* 正常的指令执行顺序:1 - 2 - 3
* 重排后指令执行顺序:1 - 3 - 2
*
* volatile功能之一:禁止指令重排
*/