• java设计模式之 -- 单例模式


    单例模式

    单例模式是GoF设计模式其中的一种并且属于创造的设计模式目录。

    单例模式限制类的实例和确保java类在java虚拟机中只有一个实例的存在。

    单例类必须提供一个全局的访问来获取类的实例。

    单例模式用来日志,驱动对象,缓存和线程池。

    单例设计模式也用在其他设计模式,例如抽象工厂,建造者,原型,门面等设计模式。

    单例模式还用在核心java中,例如java.lang.Runtime, java.awt.Desktop

    1、先写一个现场安全的单例

    public class Singleton {
        private Singleton() {}  //私有构造函数
        private volatile static Singleton instance = null;  //单例对象
        //静态工厂方法
        public static Singleton getInstance() {
              if (instance == null) {      //双重检测机制
             synchronized (Singleton.class){  //同步锁
               if (instance == null) {     //双重检测机制
                 instance = new Singleton();
                    }
                 }
              }
              return instance;
          }
    }

    代码解释:

    1.要想让一个类只能构建一个对象,自然不能让它随便去做new操作,因此Signleton的构造方法是私有的。

    2.instance是Singleton类的静态成员,也是我们的单例对象。它的初始值可以写成Null,也可以写成new Singleton()。至于其中的区别后来会做解释。

    3.getInstance是获取单例对象的方法。


    4.为了防止new Singleton被执行多次,因此在new操作之前加上Synchronized 同步锁,锁住整个类(注意,这里不能使用对象锁)

    5.进入Synchronized 临界区以后,还要再做一次判空。因为当两个线程同时访问的时候,线程A构建完对象,线程B也已经通过了最初的判空验证,不做第二次判空的话,线程B还是会再次构建instance对象。像这样两次判空的机制叫做双重检测机制

    6.为什么在单例对象加上了一个volatile ,这里涉及到了JVM编译器的指令重排

    经过volatile的修饰,当线程A执行instance = new Singleton的时候,JVM执行顺序是什么样?始终保证是下面的顺序:

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

    ctorInstance(memory);  //2:初始化对象 

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

    如果没加volatile ,JVM和CPU有可能将上面的排序进行优化

    memory =allocate();    //1:分配对象的内存空间 
    
    instance =memory;     //3:设置instance指向刚分配的内存地址 
    
    ctorInstance(memory);  //2:初始化对象 

    当线程A执行完1,3,时,instance对象还未完成初始化,但已经不再指向null。此时如果线程B抢占到CPU资源,执行  if(instance == null)的结果会是false,从而返回一个没有初始化完成的instance对象

    补充:volatile关键字不但可以防止指令重排,也可以保证线程访问的变量值是主内存中的最新值

    ps:

    如果单例初始值是null,还未构建,则构建单例对象并返回。这个写法属于单例模式当中的懒汉模式。

    如果单例对象一开始就被new Singleton()主动构建,则不再需要判空操作,这种写法属于饿汉模式

    2、用静态内部类实现单例模式

    public class Singleton {
        private static class LazyHolder {
            private static final Singleton INSTANCE = new Singleton();
        }
        private Singleton (){}
        public static Singleton getInstance() {
            return LazyHolder.INSTANCE;
        }
    }

    1.从外部无法访问静态内部类LazyHolder,只有当调用Singleton.getInstance方法的时候,才能得到单例对象INSTANCE。

    2.INSTANCE对象初始化的时机并不是在单例类Singleton被加载的时候,而是在调用getInstance方法,使得静态内部类LazyHolder被加载的时候。

    因此这种实现方式是利用classloader的加载机制来实现懒加载,并保证构建单例的线程安全。

    注意:此方法不能防止利用反射来重复构建对象。

    3、利用反射打破单例

     public static void main(String[] args) throws Exception {
            //获得构造器
            Constructor con = Singleton.class.getDeclaredConstructor();
            //设置为可访问
            con.setAccessible(true);
            //构造两个不同的对象
            Singleton singleton1 = (Singleton)con.newInstance();
            Singleton singleton2 = (Singleton)con.newInstance();
            //验证是否是不同对象
            System.out.println(singleton1.equals(singleton2));
        }

    代码可以简单归纳为三个步骤:

    第一步,获得单例类的构造器。

    第二步,把构造器设置为可访问。

    第三步,使用newInstance方法构造对象。

    最后为了确认这两个对象是否真的是不同的对象,我们使用equals方法进行比较。毫无疑问,比较结果是false。

    4、阻止反射创建单例

      1.用枚举实现单例模式:

      

    public enum SingletonEnum {
        INSTANCE;
    }
    有了enum方法,JVM会阻止反射获取枚举类的私有构造方法
    
    
    public static void main(String[] args) throws Exception {
    //获得构造器
    Constructor con = SingletonEnum.class.getDeclaredConstructor();
    //设置为可访问
    con.setAccessible(true);
    //构造两个不同的对象
    Singleton singleton1 = (Singleton)con.newInstance();
    Singleton singleton2 = (Singleton)con.newInstance();
    //验证是否是不同对象
    System.out.println(singleton1.equals(singleton2));
    }
    
    

    再次执行构造器方法

    执行结果:

     不仅可以防止反射构造对象还能报障线程安全。

    补充:使用枚举实现的单例模式,不但可以防止利用反射强行构建单例对象,而且可以在枚举类对象被反序列化的时候,保证反序列的返回结果是同一对象。

    对于其他方式实现的单例模式,如果既想要做到可序列化,又想要反序列化为同一对象,则必须实现readResolve方法。

  • 相关阅读:
    改变checkbox默认样式
    svn clean up
    vue移动端弹框组件
    移动端滚动选择
    三目运算符判断三个条件
    Vue ---- Vuex 的第一次接触
    Vue2.0组件间数据传递
    es6
    JQuery------制作div模态框
    企业知识分享+团队协作神器之Confluence
  • 原文地址:https://www.cnblogs.com/alomsc/p/13468381.html
Copyright © 2020-2023  润新知