单例设计模式-静态内部类-基于类初始化的延迟加载解决方案及原理解析

单例设计模式-静态内部类-基于类初始化的延迟加载解决方案及原理解析_第1张图片

刚刚线程1看不到线程0的重排序,我们创建一个类,这个方案是使用静态内部类来解决,一会我们也会分析一下原理,

我们创建一个静态内部类,静态内部类的代理模式,JVM在类的初始化阶段,也就是class被加载后,并且被线程使用之前,

都是类的初始化阶段,在这个阶段会执行类的初始化,那在执行类的初始化期间呢,JVM会去获取一个锁,这个锁可以同步

多个线程,对一个类的初始化,也就是绿色的部分,基于这个特性,我们可以实现基于内部类的并且是线程安全的,延迟

初始化方案,那我们看一下这个图,还是线程0和线程1,蓝色和红色,那在这种实现模式中呢,对于右侧的2和3,也就是橙色的框,

这两个步骤的重排序,对于我们前面讲的,线程1并不会看到,也就是说,非构造线程是不允许看到重排序的,因为我们之前是讲的

线程0来构造这个单例对象,初始化一个类,包括执行类的静态初始化,还有初始化在这个类中声明的静态变量,根据JAVA语言规范,

主要分5种情况,首次发生的时候呢,一个类将被立刻初始化,这里所说的类呢,泛指包括接口interface,也是一个类,那假设这个类

是A,那现在我们说一下,这几种情况,都会导致这个A类,被立刻初始化,首先呢第一种情况,有一个A类型的实例被创建,A类型中的

一个静态方法被调用,第三种情况呢,是A类中声明的一个静态成员,被赋值,第四种情况,A类中声明的一个静态成员被使用,并且这个

成员不是一个常量成员,前四种我们实际工作中用的比较多,第五种用的比较少,也就是说如果A类是一个顶级类,关于顶级类在JAVA语言

规范里面的介绍,并且呢在这个类中,有嵌套的断言语句,这种情况呢A类也会立刻被初始化,也就是说刚刚说的五种情况,前四种是我们

经常会被碰到的,只要首次发生以上说的一个情况,这个类就会被立刻初始化,把我们看一下这个图,当线程0和线程1试图想获取这个锁的

时候,也就是获得class对象的初始化锁,这个时候肯定只有一个线程能获得这个锁,假设线程0获得这个锁了,线程0执行内部类的一个初始化,

对于静态内部类即使23之间存在重排序,但是线程1是无法看到这个重排序的,因为这里有一个class对象的初始化锁,因为这里面有锁,

对于线程0而言,初始化这个静态内部类的时候,也就是把这个instance new出来,可以看到我们这里还有一个大框,所以23怎么排序

无所谓,线程1看不到,因为线程1在绿色区域等待着,所以静态内部类就是基于类初始化的延迟加载解决方案,那我们回到代码里
package com.learn.design.pattern.creational.singleton;

/**
 * 前面说了我们使用静态内部类
 * 
 * 所以静态内部类核心在于
 * InnerClass类的初始化锁
 * 看哪个线程拿到
 * 哪个线程就去初始化他
 * 对于这种情况呢
 * 我们来测试一下
 * 打开我们的线程类
 * 还是用多线程来测试
 * 
 * 非常重要的一点
 * 我们没有写私有的构造函数
 * 
 * 
 * @author Leon.Sun
 *
 */
public class StaticInnerClassSingleton {
	/**
	 * 这个class要声明成private的class
	 * 权限一定要控制好
	 * 
	 * 
	 * @author Leon.Sun
	 *
	 */
    private static class InnerClass{
    	/**
    	 * 那这里声明一个什么呢
    	 * 这个名字还是用类名来做
    	 * new一个StaticInnerClassSingleton
    	 * 那在这个静态内部类里边直接new了一个StaticInnerClassSingleton这个类的对象
    	 * 并且它是private和static的
    	 * 
    	 * 
    	 */
        private static StaticInnerClassSingleton staticInnerClassSingleton = new StaticInnerClassSingleton();
    }
    /**
     * 那我们要开放获取这个对象的方法
     * 所以是public的
     * 返回值肯定是StaticInnerClassSingleton
     * return什么呢
     * 通过这个静态内部类InnerClass点静态的成员staticInnerClassSingleton
     * 那这个静态内部类的单例模式就完成了
     * 非常简单
     * 那我们讲一下原理
     * 我们看一下图
     * 
     * 
     * @return
     */
    public static StaticInnerClassSingleton getInstance(){
        return InnerClass.staticInnerClassSingleton;
    }

    /**
     * 私有的构造器肯定是要有的
     * 否则外部就可以new出来了
     * 所以这一点千万不要忘记
     * 如果之前写的时候没有发现这个问题的话
     * 对声明构造器一定要加深
     * 现在我们来到Test直接run一下
     * 我们可以看到线程0和线程1拿到的是同一个
     * 因为这个方式非常简单
     * 就不debug了
     * 静态内部类和签名的doublecheck
     * 都是为了做延迟初始化
     * 来降低创建单例实例的开销
     * 所以具体在业务场景中采用什么方案呢
     * 还要看我们的单例对象
     * 是什么样的
     * 另外一点
     * 如果问设计模式
     * 单例模式会被百分之九十九问到
     * 那么单例模式刚刚讲的这一种方案
     * 一定要理解透
     * 这个是一个循序渐进的过程
     * 而且单例模式非常重要
     * 如果可以一层一层迭代
     * 希望对着两种方案和思路来引入静态内部类和doublecheck一步一步解决什么问题
     * 可以自己回顾一下
     * 总结一下
     * 
     * 
     * 
     */
    private StaticInnerClassSingleton(){
        if(InnerClass.staticInnerClassSingleton != null){
            throw new RuntimeException("单例构造器禁止反射调用");
        }
    }
}
package com.learn.design.pattern.creational.singleton;

public class T implements Runnable {
    @Override
    public void run() {
//        LazySingleton lazySingleton = LazySingleton.getInstance();
//        System.out.println(Thread.currentThread().getName()+"  "+lazySingleton);
//        LazyDoubleCheckSingleton instance = LazyDoubleCheckSingleton.getInstance();
    	/**
    	 * 通过这个类的getInstance方法
    	 * 看上去没有什么区别
    	 * 但是里面使用的是静态内部类
    	 * 而且是一个private的
    	 * 
    	 */
        StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();;

//        ContainerSingleton.putInstance("object",new Object());
//        Object instance = ContainerSingleton.getInstance("object");
//        ThreadLocalInstance instance = ThreadLocalInstance.getInstance();

        System.out.println(Thread.currentThread().getName()+"  "+instance);

    }
}
package com.learn.design.pattern.creational.singleton;

import java.io.IOException;
import java.lang.reflect.InvocationTargetException;

public class Test {
    public static void main(String[] args) throws IOException, ClassNotFoundException, NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
//        LazySingleton lazySingleton = LazySingleton.getInstance();

//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());
//        System.out.println("main thread"+ThreadLocalInstance.getInstance());

        Thread t1 = new Thread(new T());
        Thread t2 = new Thread(new T());
        t1.start();
        t2.start();
        System.out.println("program end");

//        HungrySingleton instance = HungrySingleton.getInstance();
//        EnumInstance instance = EnumInstance.getInstance();
//        instance.setData(new Object());
//
//        ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("singleton_file"));
//        oos.writeObject(instance);
//
//        File file = new File("singleton_file");
//        ObjectInputStream ois = new ObjectInputStream(new FileInputStream(file));
//
////        HungrySingleton newInstance = (HungrySingleton) ois.readObject();
//        EnumInstance newInstance = (EnumInstance) ois.readObject();
//
//        System.out.println(instance.getData());
//        System.out.println(newInstance.getData());
//        System.out.println(instance.getData() == newInstance.getData());

//        Class objectClass = HungrySingleton.class;
//        Class objectClass = StaticInnerClassSingleton.class;

//        Class objectClass = LazySingleton.class;
//        Class objectClass = EnumInstance.class;
////
//        Constructor constructor = objectClass.getDeclaredConstructor(String.class,int.class);
//
//        constructor.setAccessible(true);
//        EnumInstance instance = (EnumInstance) constructor.newInstance("Geely",666);


//
//        LazySingleton newInstance = (LazySingleton) constructor.newInstance();
//        LazySingleton instance = LazySingleton.getInstance();



//        StaticInnerClassSingleton instance = StaticInnerClassSingleton.getInstance();
//        StaticInnerClassSingleton newInstance = (StaticInnerClassSingleton) constructor.newInstance();

//        HungrySingleton newInstance = (HungrySingleton) constructor.newInstance();
//        HungrySingleton instance = HungrySingleton.getInstance();


//        System.out.println(instance);
//        System.out.println(newInstance);
//        System.out.println(instance == newInstance);

//        EnumInstance instance = EnumInstance.getInstance();
//        instance.printTest();


    }
}

 

你可能感兴趣的:(单例设计模式-静态内部类-基于类初始化的延迟加载解决方案及原理解析)