zoukankan      html  css  js  c++  java
  • 单例模式

    背景:单例模式模式是在编程中经常使用,他可以对需要使用的资金进行一次性初始化,防止多次初始化多次资源释放带来性能的开销。

    最近在读《JAVA并发编程的艺术》发现有些知识点不错,整理出来。

    单例模式常用模式是懒汉模式和饿汉模式

    懒汉模式:就是用到时候才new出来。

    饿汉模式:类一开始就加载好,可直接使用。

    单线程情况下,我们会通过以下实现才生成一个懒汉模式的单例模式类。但是多线程访问时候,这种实现不满足要求。

    public class Singleton {
        public static Singleton instance = null;
    
        public Singleton() {
            //todo
        }
    
        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }

    上面实现,如果在多线程情况下,可能出现多个线程同时访问instance == null 出现问题。接下来,介绍几种可以避免多线程竞争访问导致的问题。

    (一)同步处理,延迟初始化,任何时刻只有一个线程抢占到访问资源。

    public class Singleton {
        public static Singleton instance = null;
    
        public Singleton() {
            //todo
        }
    
        public static synchronized Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }

    但是这个方法也是弊端,由于采用同步处理,synchronized导致性能开销。如果getInstance()被多个线程频繁调用,会导致程序执行性能下降。反之,如果getInstance()不被多个线程频繁调用,那么这个延迟初始化将是不错的选择。

    (二)双重检查锁,优化方案一出现多线程频繁调用instance(instance!=null)时阻塞的问题。

    public class Singleton {
        public static Singleton instance = null;
    
        public Singleton() {
            //todo
        }
    
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    优点:如果instance不为null, 多线程访问instance就不会出现阻塞,提高了性能,减少了开销。

    缺点:由于JAVA内存模型,不同JIT编译器之间出现重排序问题,可能会出现线程A执行instance时instance==null,于是进入instance = new Singleton(), 线程B发现instance != null,但是这个instance可能未初始化。接下来我来细化下JMM执行过程方便理解。

    instance = new Singleton() 可以分解成以下几步:

    1. memory = allocate(); 分配对象的内存空间

    2. ctorInstance(memory); 初始化对象

    3. instance = memory; 设置instance指向刚分配的内存地址

    由于在为伪代码中2和3之间可能会出现重排序. 引入《JAVA并发编程的艺术》的图。懒得自己画了。

    鉴于这个问题,我们可以有如下更佳解决方式,把instance定义为volatile。JDK5之后引入volatile关键字,解决各个编译器处理器缓存和总线内存之间数据一致性问题。当把对象声明为volatile后,2和3重排序问题的问题,在多进程中被禁止,即不允许重排序。

    public class Singleton {
        public static volatile Singleton instance = null;
    
        public Singleton() {
            //todo
        }
    
        public static Singleton getInstance() {
            if (instance == null) {
                synchronized (Singleton.class) {
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }

    (三)如果你使用Spring来管理bean, @Autowired注释本身就是单例模式,一开始就生成好一个bean实例,即为饿汉模式。

    @Component
    public class Singleton {
        
        @Autowired
        public Singleton(....){
            //TODO
        }
    }
  • 相关阅读:
    将打开的网页以html格式下载到本地
    Shiro自定义realm实现密码验证及登录、密码加密注册、修改密码的验证
    JS或jsp获取Session中保存的值
    HTML添加上传图片并进行预览
    springMVC多图片压缩上传的实现
    DropZone图片上传控件的使用
    Wireshark安装使用及报文分析
    Spring—Document root element "beans", must match DOCTYPE root "null"分析及解决方法
    web.xml中如何设置配置文件的加载路径
    正则表达式的认识
  • 原文地址:https://www.cnblogs.com/wendy777/p/9528948.html
Copyright © 2011-2022 走看看