Java线程安全的单例模式
延迟初始化
可用静态内部类实现,原理是利用Classloader加载时的线程安全。
JVM启动时,不会一次加载所有要用到的class文件,而是根据程序需要,通过Java的类加载机制来动态加载class文件到内存中的,只有 class 文件被载入到了内存之后,才能被其它 class 所引用。
由于内部类在单独的class文件中,当外部类首次使用getInstance()方法时,触发内部类的ClassLoader,其class被加载,静态成员被初始化并赋值(静态内部类可以独立于外部类而存在;非静态内部类只有创建了外部类的实例才能使用,并且不能拥有静态成员),然后被外部类引用。从而形成延迟初始化的机制。由于Classloader加载是线程安全地,因此保证了单例的线程安全。(没有显式地用到synchronized
和lock
机制,调用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();
- new 分配空间,半初始化(填充0)
- 创建this指针,构造函数初始化(赋初始值)
- 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" 转载请保留原文链接及作者。