zoukankan      html  css  js  c++  java
  • 你真的理解了java单例模式吗?讲别人都忽略的细节!

    前言:老刘这篇文章敢做保证,java的单例模式讲的比大多数的技术博客都要好,讲述别人技术博客都没有的细节!!!

    1 java单例模式

    直接讲实现单例模式的两种方法:懒汉式和饿汉式,单例模式的概念自己上网搜吧这里就不讲了!

    这里会涉及到java中的jvm,如果你没有这方面的知识,我建议你先去补补,不然会有点迷糊!

    首先说说类什么时候进行加载?

    java虚拟机没有进行强制性的约束,但是对于初始化却严格规定了有且只有4种情况必须先对类进行初始化。

    我们要知道的是在类加载的过程中,加载、验证、准备是在初始化之前完成的,所以进行了初始化,加载、验证、准备自然就在之前完成了。

    然后这四种情况是分别遇到 new 、 getstatic 、 putstatic 和 invokestatic 这四条指令时,如果对应的类没有初始化,则要对对应的类先进行初始化。

    讲完类加载时机,就可以讲懒汉式和饿汉式了。

    直接先说说懒汉式为什么是线程不安全的?

    先看最开始的代码:

    public class Student2 {
    
        //1:构造私有
        private Student2(){}
        //2:定义私有静态成员变量,先不初始化
        private static Student2 student = null;
    
        //3:定义公开静态方法,获取本身对象
        public static Student2 getSingletonInstance(){
            //没有对象,再去创建
            if (student == null) {
                student = new Student2();
            }
            //有对象就返回已有对象
            return student;
        }   
    }

    结合之前讲的类加载内容,遇到new或加载静态方法了就会进行类加载了。

    线程1它new了一个对象,线程2它紧接着也new一个对象,第二个对象的值把第一个对象的值覆盖了,不管new了多少个对象,都会产生垃圾对象,只有最后一个对象才会保持住,其他对象都会变成不可达对象,被垃圾回收,这个过程就相当于产生了大量无效对象,这就是线程不安全的原因!

    那为了让懒汉式变得线程安全,我们要怎么做?

    看代码:

    public class Student4 {
    
        private volatile static Student4 student = null;
        private Student4() {}
    
        public static Student4 getSingletonInstance() {
            if (student == null) {//第一个null判断,是先大范围过滤一遍
                synchronized (Student4.class) {
                    if (student == null) {
                        student = new Student4();
                    }
                }
            }
            return student;
        }
    }

    这个叫双重检查锁DCL,第一个if先大范围判断是不是空值,经过synchronized,线程1先进去执行完后,线程2才能进去,然后第二个if判断是否完成创建类的实例,线程1创建完了,线程2就不用创建了。

    那为什么要加volatile关键字呢?

    因为我们Student student = new Student()的执行过程是:

    1、new触发类加载机制(已经被加载过的类不需要再次加载)

    2、分配内存空间

    3、将对象进行初始化4、讲对象引用地址赋值给栈空间中的变量但我们JVM中的JIT即时编辑器会对代码的执行过程进行优化,把过程变为1、2、4、3。

    这是什么意思呢?就是未经初始化直接赋值,这样就是student直接有值了,但整个对象还未初始化完成,所以这个对象是不完整的,是个未成品。在JVM规范中,它是一个根本不能用的对象。

    到了这个时候,线程1做了这么多事,我们让它休息会,给CPU稍微停一下,线程2就来了,它就直接得到了对象,但它调用对象的方法时,就会报错。虽然这个对象有值,但还未初始化完成。所以我们要加上volatile关键字禁止指令重新排序。

    面试重灾区说的差不多了,饿汉式还是要讲讲。

    最后就说说饿汉式为什么没有线程安全问题?​

    看代码:

    public class Student1 {
        // 2:成员变量初始化本身对象
        private static Student1 student = new Student1();
        // 构造私有
        private Student1() {
        }
        // 3:对外提供公共方法获取对象
        public static Student1 getSingletonInstance() {
            return student;
        }
        public void sayHello(String name) {
            System.out.println("hello," + name);
        }
    }

    根据类加载的东西,在多线程的条件下,线程1先执行getSingletonInstance()时,就会进行类加载,类的静态资源就会进行初始化。根据JVM安全机制里说的,当一个类被JVM加载的时候,该类的加载是线程安全的,相当于JVM对该过程加锁了。所以整个过程处于一个锁的范围内,然后静态成员变量进行初始化就相当于Student1()被new了,只会被new一次。

    当第二个线程进来,它就发现这个类已经被加载了,就不需要进行加载了,对象也不需要频繁创建,所以线程是安全的!

    2 总结

    老刘看过很多关于java单例模式的资料,多多少少都会缺少一点细节,这次老刘把它补全了。

    最后,如果觉得有哪里写的不好或者有错误的地方,可以联系公众号:努力的老刘,进行交流。

    如果觉得写的不错,给老刘点个赞!

     

  • 相关阅读:
    Ajax中onreadystatechange函数不执行,是因为放在open()后
    js调用ajax案例2,使用ok
    js调用ajax案例
    通过设置ie的通过跨域访问数据源,来访问本地服务
    Net 4.5 WebSocket 在 Windows 7, Windows 8 and Server 2012上的比较以及问题
    Net 4.5 WebSocket 在 Windows 7, Windows 8 and Server 2012上的比较
    windows 系统纯净版官网下载地址
    iOS:给Git仓库上传代码时,超过100M会被拒绝(例如github和oschina)
    iOS:Xcode8以下真机测试iOS10.0和iOS10.1配置包
    iOS:高德地图的使用
  • 原文地址:https://www.cnblogs.com/bigdatalaoliu/p/14180874.html
Copyright © 2011-2022 走看看