• 单例模式 之 单例模式——懒汉模式


    懒汉模式:和饿汉模式不同,懒汉模式并不会一开始声明对象,而是需要等到调用时再声明对象。他很懒,所以你叫“它”它才会动...

    代码1:

    /**
     * 懒汉模式
     */
    public class LazybonesSingleton1 {
        //先声明
        private static LazybonesSingleton instance;
        /**
         * 禁止外部构建
         */
        private LazybonesSingleton(){}
    
        /**
         * 对外提供调用方法
         * @return
         */
        public static LazybonesSingleton getInstance() {
            if(instance==null){
                instance=new LazybonesSingleton();
            }
    
            return instance;
        }
    
        /**
         * 测试
         * @param args
         */
        public static void main(String[] args) {
            for(int i=0;i<20;i++){
                new Thread(()->{
                    System.out.println(LazybonesSingleton.getInstance());
                }).start();
            }
        }
    
    
    }

    代码1 测试结果:发现 第一个线程运行时 和剩下线程运行 ,并不是一个单例。

    这是因为多线程时,很有可能出现两个或多个线程同时执行。

    也就是说他们同时运行到了 if(instance==null) 所以都创建了一个对象的实例

    com.company.LazybonesSingleton@55477623
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    
    Process finished with exit code 0

    代码2:根据上述问题,我们进行了优化  getInstance 使用了 synchronized

    /**
     * 懒汉模式
     */
    public class LazybonesSingleton2 {
        //先声明
        private static LazybonesSingleton instance;
        /**
         * 禁止外部构建
         */
        private LazybonesSingleton(){}
    
        /**
         * 对外提供调用方法
         * @return
         */
        public static synchronized LazybonesSingleton getInstance() {
            if(instance==null){
                instance=new LazybonesSingleton();
            }
    
            return instance;
        }
    
        /**
         * 测试
         * @param args
         */
        public static void main(String[] args) {
            for(int i=0;i<20;i++){
                new Thread(()->{
                    System.out.println(LazybonesSingleton.getInstance());
                }).start();
            }
        }
    
    
    }

    代码2 测试结果:运行结果为同一个用例,保证了安全性。

    但是这里出现了一个问题 synchronized 会让 线程变成串行执行synchronized后,其他线程会在外进行等待,直到运行结束。再有下一个线程运行,剩下未执行的线程继续等待,同样直到该线程运行结束 )。

    虽然保证了安全性,但是性能却很差。

    com.company.LazybonesSingleton@3996d457。
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    com.company.LazybonesSingleton@3996d457
    
    Process finished with exit code 0

    代码3:我们再进行优化,将 synchronized  放到 if(instance==null) 里面

    这样其他线程就不用在执行 getInstance() 时进行等待了,性能也有所改进。

    /**
     * 懒汉模式
     */
    public class LazybonesSingleton3 {
        //先声明
        private static LazybonesSingleton instance;
        /**
         * 禁止外部构建
         */
        private LazybonesSingleton(){}
    
        /**
         * 对外提供调用方法
         * @return
         */
        public static  LazybonesSingleton getInstance() {
            if(instance==null){
                synchronized (LazybonesSingleton.class){
                    instance=new LazybonesSingleton();
                }
            }
    
            return instance;
        }
    
        /**
         * 测试
         * @param args
         */
        public static void main(String[] args) {
            for(int i=0;i<20;i++){
                new Thread(()->{
                    System.out.println(LazybonesSingleton.getInstance());
                }).start();
            }
        }
    
    
    }

    代码3 测试结果:虽然性能有所改进,但是依然存在安全问题,

    原因在于:多线程运行过程中很有可能同时运行到 if(instance==null),虽然会在外面等待由另一个线程执行完再执行,所以也会存在创建多个实例的问题

    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    
    Process finished with exit code 0

    -----------------------------以下是我运行多次的结果-----------------------------------

    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@55477623
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707
    com.company.LazybonesSingleton@4f0c4707

    Process finished with exit code 0




    代码4:在 synchronized 中 再判断一次是否为空,这样便能解决 代码3产生的问题。

    此种方式 可以叫 双重检测(DCL),便能解决 懒加载的安全问题

    /**
     * 懒汉模式
     */
    public class LazybonesSingleton {
        //先声明
        private static LazybonesSingleton instance;
        /**
         * 禁止外部构建
         */
        private LazybonesSingleton(){}
    
        /**
         * 对外提供调用方法
         * @return
         */
        public static  LazybonesSingleton getInstance() {
            if(instance==null){
                synchronized (LazybonesSingleton.class){
                    if(instance==null){
                        instance=new LazybonesSingleton();
                    }
                }
            }
    
            return instance;
        }
    
        /**
         * 测试
         * @param args
         */
        public static void main(String[] args) {
            for(int i=0;i<20;i++){
                new Thread(()->{
                    System.out.println(LazybonesSingleton.getInstance());
                }).start();
            }
        }
    
    
    }

    代码4 运行结果:

    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    com.company.LazybonesSingleton@19281561
    
    Process finished with exit code 0
  • 相关阅读:
    Java反射获取对象VO的属性值(通过Getter方法)
    HTML的级联Select
    ORACLE 新增记录 & 更新记录
    ORACLE 仿照原表建表语法
    ActiveMQ反序列化漏洞(CVE-2015-5254)复现
    滥用DNSAdmins权限进行Active Directory提权
    Weblogic CVE-2018-3191远程代码命令执行漏洞复现
    Libssh认证绕过CVE-2018-10933漏洞复现
    Linux kernel(CVE-2018-17182)提权漏洞复现
    时间延迟盲注详解
  • 原文地址:https://www.cnblogs.com/zhangzhonghui/p/11460731.html
Copyright © 2020-2023  润新知