Java线程安全的单例模式

  1. 延迟初始化
  2. 非延迟初始化
  3. DCL 双重检查锁

延迟初始化

可用静态内部类实现,原理是利用Classloader加载时的线程安全。

JVM启动时,不会一次加载所有要用到的class文件,而是根据程序需要,通过Java的类加载机制来动态加载class文件到内存中的,只有 class 文件被载入到了内存之后,才能被其它 class 所引用。

由于内部类在单独的class文件中,当外部类首次使用getInstance()方法时,触发内部类的ClassLoader,其class被加载,静态成员被初始化并赋值(静态内部类可以独立于外部类而存在;非静态内部类只有创建了外部类的实例才能使用,并且不能拥有静态成员),然后被外部类引用。从而形成延迟初始化的机制。由于Classloader加载是线程安全地,因此保证了单例的线程安全。(没有显式地用到synchronizedlock机制,调用getInstance()方法的执行效率高。)

public class MySingleton {

    //内部类
    private static class MySingletonHandler{
        private static MySingleton instance = new MySingleton();
    }

    private MySingleton(){}

    public static MySingleton getInstance() {
        return MySingletonHandler.instance;
    }
}

非延迟初始化

直接定义静态成员变量就好了,原理也是类加载机制的线程安全。

public class MySingleton {
    private static MySingleton instance = new MySingleton();

    private MySingleton() {}

    public static MySingleton getInstance() {
        return instance;
    }
}

DCL 双重检查锁

双重检查锁(Double Check Lock)存在指令重排序问题,当new 的语句发生指令重排时,会出现半初始化的对象被访问的情况。
对象的创建过程:Object t = new Object();

  1. new 分配空间,半初始化(填充0)
  2. 创建this指针,构造函数初始化(赋初始值)
  3. t 指向被分配的空间。

2,3 可能发生指令重排,导致3先执行,从而t指向半初始化的空间,未加锁的线程越过了 INSTANCE ==null 的判断取回了半初始化对象。

基于2的顺序,所以对象创建过程中不要创建线程并在线程中使用this,因为this指向值可能尚未完成构造函数的初始化。

参考:
https://blog.csdn.net/cselmu9/article/details/51366946
https://blog.csdn.net/sted_zxz/article/details/76959200
https://segmentfault.com/a/1190000008491597


转载请注明来源,欢迎对文章中的引用来源进行考证,欢迎指出任何有错误或不够清晰的表达。可以在下面评论区评论,也可以邮件至 using1174@foxmail.com

文章标题: Java线程安全的单例模式

文章字数: 559

本文作者: Jun

发布时间: 2019-05-14, 15:11:00

最后更新: 2022-04-08, 14:41:44

原始链接: http://yoursite.com/2019/05/14/Java线程安全的单例模式/

版权声明: "署名-非商用-相同方式共享 4.0" 转载请保留原文链接及作者。

目录
×

喜欢就点赞,疼爱就打赏