美文网首页
剑指offer——面试题2:实现Singleton模式

剑指offer——面试题2:实现Singleton模式

作者: 金锡圭璧 | 来源:发表于2020-01-06 20:19 被阅读0次

    1. 懒汉式:

    public class Singleton {
        //1. 本类内部创建对象实例
        private static Singleton instance = null;
    
        //2. 构造方法私有化,不允许外部new
        private Singleton(){}
    
        //3. 提供一个公有的静态方法,返回实例对象
        public static Singleton getInstance() {
            if (instance == null) {
                instance = new Singleton();
            }
            return instance;
        }
    }
    

    懒汉式单例模式在调用时先判断实例是否存在,如果存在就直接使用,如果不存在就创建一个实例。(以时间换空间)
    不加同步的懒汉式是线程不安全的,因为可能同时两个线程都判断到对象不存在,然后都new一个对象。
    因此,需要使用双重检查加锁的方法。
    一个线程想要访问对象,必须先获取到对象的监视器(monitor),synchronized最大的特征就是在同一时刻只有一个线程能够获得对象的监视器。
    用volatile修饰对象实例用来限制指令重排序。Java类初始化过程是非原子操作,会经历三个阶段:
    (1)分配内存给这个对象
    (2)初始化对象
    (3)将引用指向刚分配的内存空间
    其中(2)和(3)可能会指令重排序,也就是说执行顺序可能是123也可能是132。所以如果执行顺序是132,那么当线程执行完3,此时对象已经不为空,但是并没有初始化。此时另一个线程抢占了CPU,那么执行getInstance()方法时就会直接返回instance(非 null 但却没有初始化),后续使用就会出错了。

    public class Singleton {
        //1. 本类内部创建对象实例,加volatile禁止指令重排序
        private static volatile Singleton instance = null;
    
        //2. 构造方法私有化,不允许外部new
        private Singleton(){}
    
        //3. 提供一个公有的静态同步方法,返回实例对象
        public static synchronized Singleton getInstance() {
            //4.第一次判空检查,主要是为了效率,当instance为null的时候,才进入synchronized代码段
            if (instance == null) {    
                //5. new之前加锁
                synchronized (Singleton.class) {
                    //6. 第二次判空检查,主要是为了防止可能出现多个实例
                    if (instance == null) {
                        instance = new Singleton();
                    }
                }
            }
            return instance;
        }
    }
    

    2. 饿汉式:

    public class Singleton {
        //1. 创建实例
        private static Singleton instance = new Singleton();
    
        //2. 构造方法私有化,不允许外部new 
        private Singleton() {}
    
        //3. 提供一个公有的静态同步方法,返回实例对象
        public static Singleton getInstance() {
            return singleton;
        }
    }
    

    饿汉式在类加载的时候就会创建实例,在调用时不需要再判断了。(以空间换时间)

    3. 使用静态内部类实现单例模式:

    public class Singleton {
        //1. 静态内部类,只会在虚拟机装载类的时候初始化一次
        private static class SingletonHolder {
            private static Singleton instance = new Singleton();
        }
    
        //2. 构造方法私有化,不允许外部new
        private Singleton(){}
    
        //3. 提供一个公有的静态同步方法,返回实例对象
        public static Singleton getInstance() {
            return SingletonHolder.instance;
        }
    }
    

    当getInstance方法第一次被调用的时候,它会返回SingletonHolder.instance,导致SingletonHolder类得到初始化;而这个类在装载并被初始化的时候,会初始化它的静态域,从而创建Singleton的实例,由于是静态的域,因此只会在虚拟机装载类的时候初始化一次,并由虚拟机来保证它的线程安全性。

    参考&鸣谢:

    -《java 单例模式的几种实现方式》
      https://blog.csdn.net/fd2025/article/details/79711198
    -《单例模式中的懒汉式以及线程安全性问题》
      https://www.cnblogs.com/sunnyDream/p/8011186.html

    相关文章

      网友评论

          本文标题:剑指offer——面试题2:实现Singleton模式

          本文链接:https://www.haomeiwen.com/subject/tjzkactx.html