package 单例模式.懒汉模式.懒汉单例速成;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
//线程应该是有自己的地址空间他的地址空间是cpu调度进程的空间分配给他的不然说不通
public class SpeedLazy {
//分4步
//1.保证单线程(1.单线程先保证他不会被new 出来只能通过我们的静态类方法获得(需要时再获得))
//2.保证多线程
//3.保证反射
//4枚举(枚举不谈,枚举默认构造反编译为双参数构造且不能被反射)
//单线程已经完毕
//接下来是多线程
//在多线程执行时每个线程会将主线程的类复制去各自线程
//那么各自线程就都相当于没有创建过实例对象 那么他们就都可以创建实例对象也就是new
//那么在各自线程创建之前我们是不是应该先检测一下他有没有被创建了所以接上一步给他加的判断if不变(先保证各自线程都是一份)
//因为我们的static SpeedLazy SPEEDLAZY 已经保证我们要查看使用的这个变量 他所有线程共享一份所以他可以直接检测到SPEEDLAZY是不是存在着
//因为cpu创建对象很快在创建对象步骤有可能会几个线程一起创建那么就完了
//要破解就加锁---给谁加锁?我们现在是通过类方法调用创建//那么就只有一个通道-->类方法
//那么我们肯定不能锁方法因为这个线程执行完这个方法后各个线程他依旧会依次执行只是当前有别的线程执行时无法进入
//那么在方法里锁谁?--锁这个SpeedLazy类的步骤让要使用这个步骤的线程都给我等着
//我们一个走,那么如过我们第一线程创建好了第二线程是不是要检测一下?synchronized里再判断有没有线程已经创建过了对象因为后面都已经在排队就剩
// 创建对象了
//好了这下就2null判断-鎻了DCL懒汉就完成了
//但是多线程还有问题那还是cpu的锅我们创建对象不是1分配空间 2启动构造初始化对象 3将对象指向分配的空间 但他又有可能走出1 3 2步骤
//对象有可能是虚无的创建只有空间地址的躯壳 别的线程万一使用了就无了
//为了保证static变量的安全 我们要加volatile保证可见性 看他到底创建好没有
// 最后我们在构造方法中加上各自线程名查看一下是否是单例
private static boolean exist=false;
private SpeedLazy(){
if (!exist) {
exist=true;
synchronized (SpeedLazy.class) {
if (SPEEDLAZY == null) {
System.out.println(Thread.currentThread().getName());
} else {
throw new RuntimeException();
}
}
}else{
throw new RuntimeException("妄图多反射?");
}
}
private static volatile SpeedLazy SPEEDLAZY;
public static SpeedLazy getInstance(){
if (SPEEDLAZY==null) {//不,我重新考虑了一下这个判断是判断是否有类方法创建的对象
synchronized (SpeedLazy.class) {//鎻这个类保持类单例,然后再判断其他线程是否有这个类的类方法创建的对象
if (SPEEDLAZY == null) {
SPEEDLAZY = new SpeedLazy();
}
}
}
return SPEEDLAZY;
}
public static void main(String[] args) throws NoSuchMethodException, InvocationTargetException, InstantiationException, IllegalAccessException {
// new Thread(()->{SpeedLazy.getInstance();}).start();
// new Thread(()->{SpeedLazy.getInstance();}).start();
// 多线程完美单例
// System.out.println(SpeedLazy.getInstance());
Constructor declaredConstructor = SpeedLazy.class.getDeclaredConstructor(null);
declaredConstructor.setAccessible(true);
SpeedLazy speedLazy = declaredConstructor.newInstance();
System.out.println(speedLazy);
SpeedLazy speedLazy1 = declaredConstructor.newInstance();
System.out.println(speedLazy1);
}
}
//最后我们是通过反射来创建对象
//他跟类方法创建是两个地址不是单例
//反射可以使私有构造通过设置实例化对象
//可以在私有构造中加锁并判断是否在进程中有这个类的实例化对象若有侧抛出异常
//没有就正常创建,这样他就不能用类方法创建对象后再用反射创建了,但是双反射却又可以创建对象
//他们也是两个不同的地址,那么为什么他可以创建2个对象?
//看我重新思考的地方,那么这下就说的清了他在私有构造中也是先锁类让各都 等待然后判断是否有类方法创建的对象对象名字是固定的所以我们
//反射他首先不是类方法创建的而且对象名不一样
//所以我们就可以反射创建多个对象
//那么怎么防止呢?
//在类中加静态变量只要我们一通过构造创建那么我们就让变量变化
//那么变量变化了我们再去检测他是否变了,没变就让他创建,变了就不让他创建
//最后他其实还可以被破坏还是反射我们的变量不是私有么,通过反射设置让他变化也可以再多反射
//这时候直接用枚举先方法就行了
//枚举自带防反射