多线程学习之静态内部类实现单例模式《七》

介绍之前先简单的说一下内部类:

无论是静态内部类还是非静态内部类,在类初始化的时候都不会被加载
package duoxiancheng.single.neibulei;

public class ClassMonitor {


    /*
        //1. 如果在懒汉单例模式里面添加一个它的全局属性
        //2. 如果想获取它的yourClass 可以调用直接调用yourClass
        //3. 但是这样对象会初始化,构造方法初始化,我不想让类初始化怎么办
        public static String yourClass = "通信工程1班";

        private static ClassMonitor classMonitor = new ClassMonitor();

        private ClassMonitor() {
            System.out.println("构造方法初始化");
        }

        public static ClassMonitor getClassMonitor() {
            System.out.println("获取对象");
            return classMonitor;
        }

        public static void main(String[] args) {

            System.out.println(yourClass);
        }
    */


    /*
      1. 当执行getInstance()的时候才会调用内部类里面的ClassMonitor实例
      2. 此时,内部类会被加载到内存中,在类加载的时候对monitor进行初始化
      3. 而且直接调用yourClass的时候不会对类初始化
      4. 这是懒汉模式和恶汉模式的结合体,而且线程安全
*/
    public static String yourClass = "通信工程";

    //静态内部类,用来创建班长对象
    private static class MonitorCreator {
        private static ClassMonitor classMonitor = new ClassMonitor();

    }

    private ClassMonitor() {
        System.out.println("构造方法初始化");
    }

    public static ClassMonitor getInstance() {
        System.out.println("静态内部类获取");
        return MonitorCreator.classMonitor;
    }

    public static void main(String[] args) {
        System.out.println(yourClass);
    }


}

为什么静态内部类能保证单例?

我们再回头看下getInstance()方法,调用的是MonitorCreator.classMonitor,
取的是SingleTonHoler里的INSTANCE对象,
跟上面那个DCL方法不同的是,getInstance()方法并没有多次去new对象,
故不管多少个线程去调用getInstance()方法,取的都是同一个INSTANCE对象,而不用去重新创建。
当getInstance()方法被调用时,SingleTonHoler才在SingleTon的运行时常量池里,把符号引用替换为直接引用,
这时静态对象INSTANCE也真正被创建,然后再被getInstance()方法返回出去,这点同饿汉模式。
 
那么,是不是可以说静态内部类单例就是最完美的单例模式了呢?
其实不然,静态内部类也有着一个致命的缺点,就是传参的问题,由于是静态内部类的形式去创建单例的,
故外部无法传递参数进去,例如Context这种参数,
所以,我们创建单例时,可以在静态内部类与DCL(双重双重锁懒汉模式(Double Check Lock))模式里自己斟酌。
静态内部类实现的单例模式为什么是线程安全的
 虚拟机会保证一个类的<clinit>()方法在多线程环境中被正确地加锁、同步,如果多个线程同时去初始化一个类,那么只会有一个线程去执行这个类的<clinit>()方法,其他线程都需要阻塞等待,直到活动线程执行<clinit>()方法完毕。如果在一个类的<clinit>()方法中有耗时很长的操作,就可能造成多个进程阻塞(需要注意的是,其他线程虽然会被阻塞,但如果执行<clinit>()方法后,其他线程唤醒之后不会再次进入<clinit>()方法。同一个加载器下,一个类型只会初始化一次。),在实际应用中,这种阻塞往往是很隐蔽的。
 

猜你喜欢

转载自blog.csdn.net/qq_39455116/article/details/86634543