美文网首页
单例模式

单例模式

作者: Jorvi | 来源:发表于2018-11-16 09:56 被阅读0次

    参考:
    https://www.cnblogs.com/zhaoyan001/p/6365064.html


    1. 恶汉式

    public class Singleton {
        private static final Singleton instance = new Singleton();
    
        private Singleton() {
        }
    
        public static Singleton getInstance() {
            return instance;
        }
    }
    

    利用静态成员常量创建单例模式,简单、线程安全。

    原理
    类的静态常量,在类加载过程的准备阶段已经完成初始化,由JVM控制。


    2. 懒汉式

    public class Singleton {
        private static Class Inner {
            public static Singleton instance = new Singleton();
        }
    
        private Singleton() {
        }
    
        public static Singleton getInstance() {
            return Inner.instance;
        }
    }
    

    利用静态内部类创建单例模式,线程安全。

    原理
    一、根据Java语言规范,在首次发生下列任意一种情况时,一个类或接口类型T将被立即初始化(类加载的初始化阶段,此时加载、验证、准备已在此之前开始)。

    1. T是一个类,而且一个T类型的实例被创建;
    2. T是一个类,而且T中声明的一个静态方法被调用;
    3. T中声明的一个静态字段被赋值;
    4. T中声明的一个静态字段被使用,且这个字段不是一个常量字段;
    5. T是一个顶级类,而且一个断言语句嵌套在T内部被执行。

    二、Java语言规范规定,对于每一个类或接口C,都有一个唯一的初始化锁LC与之对应。

    1. 当两个线程A和B试图同时初始化一个类(Class对象)时,会先去竞争Class对象的初始化锁,假设线程A获取到了初始化锁,那么线程B将一直等待获取初始化锁;
    2. 线程A发现Class对象还没有被初始化,开始执行类的静态初始化和初始化类中声明的静态字段;
    3. 线程A初始化完毕,唤醒所有等待初始化锁的线程,然后释放初始化锁;
    4. 线程B获取到初始化锁,根据锁的happens-before关系,可以确保线程A执行类的初始化时的写入操作,线程B一定可见,于是线程B会发现类已经初始化完毕,线程B释放初始化锁。

    利用初始化锁控制线程安全性。


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

    利用volatile+双重检查创建单例模式,线程安全。

    原理
    创建一个对象instance = new Singleton(),大致分为:

    1. 分配对象的内存空间;
    2. 初始化对象;
    3. 设置instance指向分配的内存地址。

    如果不使用volatile变量,而是用普通变量的话:
    在单线程中,2 和 3 是可以重排序的,并不影响结果。
    在多线程中,2 和 3 重排序将可能导致未初始化完毕的对象在最外面一层判断null == instance时为false,而误认为对象已经初始化完毕了。

    利用volatile将禁止2 和 3 的重排序,保证instance != null时,对象必定已经初始化完成。

    相关文章

      网友评论

          本文标题:单例模式

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