• 设计模式之单例模式


         好久没写过博客了,最近准备把自己对设计模式的理解写下来,既是作为分享也是为了记录。这一篇我们来一块聊聊单例模式。

    单例模式的核心点就是只创建一个实例,我们先看下如何实现的。

    package com.guantong.seeing.screening.common;
    
    /**
     * 单例模式
     * cxx
     */
    public class Singleton {
        /**
         * 私有静态实例
         */
        private static Singleton instance = null;
    
        /**
         * 私有构造方法,防止被实例化
         */
        private Singleton() {
        }
    
        /**
         * 创建唯一实例
         * @return
         */
        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    
    }

       这样就实现了最初版本的单例模式,我们来写个测试类,验证下是否是单例。

    package com.guantong.seeing.screening.common;
    
    import org.junit.Test;
    
    public class SingletonTest {
    
        @Test
        public void testGetInstance()   {
            Singleton result = Singleton.getInstance();
            Singleton result1 = Singleton.getInstance();
            System.out.println(result);
            System.out.println(result1);
        }
    }

    输入结果:

     可以看到,内存地址都是一致的,证明是一个单例模式

            但是这是单线程的单例模式,如果处于高并发的环境,同时有两个线程F1,F2进入到了getInstance方法,我们来写段代码看看会发生什么

      @Test
        public void testGetInstance1() throws InterruptedException {
    
           new Thread(() -> {
                Singleton instance = Singleton.getInstance();
                System.out.println(instance);
            }).start();
    
          new Thread(() -> {
                Singleton instance = Singleton.getInstance();
                System.out.println(instance);
            }).start();
    
    
        }

    通过跑多线程环境,我发现这个单例被破坏了。也就是说,当两个线程同时 创建实例的时候,单例就被破坏了,我们可以对这个单例模式加个锁

     /**
         * 创建唯一实例
         * @return
         */
        public static  synchronized Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }

    但是这样性能不好,我们可以把锁的范围调小点。


    /**
    * 创建唯一实例
    *
    * @return
    */
    public static Singleton getInstance() {
    if (instance == null) {
    synchronized (instance) {
    if (instance == null) {
    instance = new Singleton();
    }
    }
    }
    return instance;
    }

    这样解决了我们上面的问题,但是又有一个新问题浮现了。

    由于jvm的对象创建机制是  分配地址和初始化是随机顺序的。所以当线程F1被jvm分配内存之后,并且未初始化Singleton之间,F2请求进来了,这时候F2通过代码判断已经有了实例了,便直接拿去用了,

    问题就出现了,F2拿去的可能是空的实例,会导致空指针等问题。

    这时候我们就需要 volatile锁了

    package com.guantong.seeing.screening.common;
    
    /**
     * 单例模式
     * cxx
     */
    public class Singleton {
        /**
         * 私有静态实例
         */
        private static volatile Singleton instance = null;
    
        /**
         * 私有构造方法,防止被实例化
         */
        private Singleton() {
        }
    
        /**
         * 创建唯一实例
         *
         * @return
         */
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (instance) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    
    }

    值得注意的是,volatile控制的并不是jvm分配内存地址和初始化的顺序,而是在这两步没完成前不会去调用  if (instance == null) 这一步

  • 相关阅读:
    23种设计模式之单例模式
    Java基础之IO技术(一)
    spark利用yarn提交任务报:YARN application has exited unexpectedly with state UNDEFINED
    linux配置了dns后导致mysql远程连接慢问题
    发布网站后localhost可以访问ip不行
    electron编译sqlite3
    vscode文件树缩进
    js判断浏览器类型
    vue可复用slide动画
    vscode使用formate格式化less遇到的坑
  • 原文地址:https://www.cnblogs.com/blogsofmy/p/12982695.html
Copyright © 2020-2023  润新知