单例的意义:不同线程拿到对象时得到是同一个对象。
单例的应用场景: 创建工厂。
单例的实现方式:
1.饥汉式:通过 初始化静态变量、私有构造方法,返回当前实例,
缺点:不调用也会进行实例化
public class SingleDome1 {
// 初始化静态变量
private static final SingleDome1 SINGLE_DOME_1 = new SingleDome1();
// 私有化构造方法
private SingleDome1(){};
// 当前实例
public static SingleDome1 getSingleDome1 (){
return SINGLE_DOME_1;
}
}
2.懒汉式 :延迟加载 只有在调用的时候才会加载实例化
class LazySingleton {
// 字节码层执行顺序(正常)
// 1.分配空间
// 2.初始化
// 3.引用赋值
// JIT / CPU 的影响都可能发生指令重排(最先执行第一步,第二/第三步的执行顺序可能会被打乱,
// 指令重排对单线程中是没有影响的,但是对多线程的影响可能会发生赋值完后但是没有初始化,比如在单例模式中会最后导致空指针的问题)
// 赋值完后没有初始化的真正原因: 假设单例模式下 T1进入方法中咋进行先赋值未初始化的操作,T2在判断中检验对象已经是有值的,
// 并且在T1初始化之前T2将对象返回出去,这时候T2拿到的就会是一个空对象导致空指针的问题.
// JAVA 中的volatile 可以防止重排序问题 一定会进行 123的方式进行执行.
// 私有化对象 不赋值
private volatile static LazySingleton instance;
// 私有化构造
private LazySingleton (){}
// 拿到实例
public static LazySingleton getLazySingleton(){
if(instance == null){
// 给当前对象加锁
synchronized (LazySingleton.class){
if(instance != null){
return instance;
}
instance = new LazySingleton();
System.out.println("new了一次对象");
}
}
return instance;
}
}
懒汉式通过jvm特性实现 :外部类加载时不会加载内部类
public class SingleStatic {
//私有构造
private SingleStatic(){};
// 私有静态内部类 第一次调用时实例化对象
private static class SingleNew {
private static final SingleStatic SINGLE_STATIC = new SingleStatic();
}
//拿到对象
public static SingleStatic getSingleStatic(){
return SingleNew.SINGLE_STATIC;
}
}
3.枚举单例(懒汉式)
// 枚举单例因为没有构造方法,可以避免反射new出新的单例
public enum singleEnum {
// 对象枚举
INSTANCE;
// 测试
public static void main(String[] args) {
for (int i = 0; i <100 ; i++) {
new Thread(() ->{
System.out.println(singleEnum.INSTANCE.hashCode());
}).start();
}
}
}
springBean工厂也可以实现单例模式。