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

    创建型:Singleton(单例模式)

      单例模式,或者称为元件模式。一般来说,在所有模式中,属于最小代码实现的翘楚。刚找工作那会,经常在笔试题遇到写出你知道的设计模式,基本上单例是必写的,不为啥,至少占地面积小。

      一般我们有这两种实现:

    package top.gabin.oa.web.design.singleton;
    
    /**
     * 简单单例模式示例
     * @author linjiabin on  16/5/4
     */
    public class SimpleSingleton {
        private static  Object singleton = new Object();
        private static Object singleton2;
    
        public static Object getSingleton() {
            return singleton;
        }
    
        public static Object getSingleton2() {
            if (singleton2 == null) {
                singleton2 = new Object();
            }
            return singleton2;
        }
    
    }

      静态变量在整个应用中只会持有一份对象,而全局访问点也只有一个,这就是我们一般定义的单例了:保证只有一个对象,一般也只有一个全局访问点。至于初始化的时间,要看创建对象的资源损耗和使用频率。一般使用频率高,损耗低的会直接初始化。

      当然看起来简单的东西并不见得容易维护,其实单例模式往往还要解决并发访问的问题,这不在讨论范围,并且我也对并发没有那么深的见解。

    *************************************************************************************

    *************************************************************************************

    2020.07.27重读设计模式后:

    单例模式的单例类有两个职责:

    1、提供全局访问点

    2、负责系统中的某个组件功能

    一般有两种初始化单例对象的方式:

    1、立即初始化,jvm会保证单例对象只有一个,但不包括同时存在两个或以上ClassLoader的情况下(多个ClassLoader会导致类被多次加载);且在jdk1.2之前的版本,会出现单例被吃掉的情况(GCC认为单例没有被引用,必须实现注册表接口)

    2、延迟初始化,需要的时候才初始化;会导致并发问题,不在乎性能的情况下,可以直接在方法上加synchronized;或者也可以采用双重检查锁的方式,如以下的例子

    package top.gabin.patterns;
    
    // 双重检查锁示例
    public class SimpleSingleton {
    
        // 2、静态变量,一个类只有一个,多个实例也是共用一个;
        // volatile告诉虚拟机和编译器这里不要做指令重排
        private static volatile SimpleSingleton simpleSingleton;
    
        // 1、私有化构造器,使得外部不能新建实例
        private SimpleSingleton() {
    
        }
    
        // 3、全局访问点
        public static SimpleSingleton getInstance() {
            if (simpleSingleton == null) {
                synchronized (SimpleSingleton.class) {
                    if (simpleSingleton == null) { // 同步之后,需要再判断一次,避免其他线程修改
                        simpleSingleton = new SimpleSingleton();
                    }
                }
            }
            return simpleSingleton;
        }
    }

    ******************************************************************************************************

    有些面试中会问这里需不需要加volatile,一般大家也都知道,要加,但是为什么要加,可能就说不是很清楚

    或者呢,会说个指令重排序。

    但再往内讲呢,如果想逼格高点地回答,可以扯到

      1、Class二进制文件中,对初始化执行的指令不是原子性,有多条指令

      2、其中有两条指令有可能被重排序,这两条指令执行的大概一个是初始化(非实例化)对象,另一个是给变量赋值(把new出来的对象关联到引用变量)。一般来说,按照这个顺序执行是没有问题的,但是呢CPU是会对指令重排序的

      3、重排序之后,有可能先把实例化好(但未初始化的对象)和变量关联起来,那么这个时候单例的对象就不为空了,可是呢,实际上这个对象还不完整,可能里面的属性值都还没设好

      ps:对象的初始化过程:

          - 申请内存

          - 实例化,为属性|域设置默认值

          - 初始化,为属性|域设置初始值

    所以加volatile是为了上面加红的地方不被乱序执行

    ******************************************************************************************************

    另外再补充一点,静态变量初始化为什么能保证单例呢,以及在什么情况下,会失效呢

    这里就要扯到JVM中ClassLoader的机制中的双亲委派机制

    因为这种机制可以保证同一个全限定名称的类只会被加载一次,静态变量自然也就只有一份。

    可是呢,这个机制其实可以被打破的。说起来要理解的概念就比较多了。

    我们简单来理解下

    1、假设ClassLoader将Class加载到内存,大家都知道我们经常会这样用Object.class。也就是说这其实也是个对象

    2、既然是对象,为什么不会new多次呢,其实它也是可以被new多次的(new只是一种概念,作者不太清楚是否ClassLoader也是new的概念)。只不过Java设计的时候ClassLoader的机制用了模板方法区实现,除非我们故意重写其中的一个LoadClass的方法,否则这些个类对象都是单例模式的实现

    那基于上面的简单理解,在我们打破这种单例模式的前提之下,我们多次LoadClass,不就使得类被加载多次了吗。那么这种情况下,其实静态变量的单例模式实现,可能就有问题了

  • 相关阅读:
    linux下安装elasticsearch5.6.3
    linux下安装git
    环境安装备忘录 Zookeeper
    环境安装备忘录 JDK
    环境安装备忘录 Tomcat
    MySql 通过show status 优化数据库性能
    MySQL执行计划解读 转他人文章
    2015年12月21日 my.cnf 配置
    mysql 如何查看my.cnf的 位置
    mysql状态查看 QPS/TPS/缓存命中率查看
  • 原文地址:https://www.cnblogs.com/gabin/p/5457122.html
Copyright © 2011-2022 走看看