我是靠谱客的博主 文艺黑米,最近开发中收集的这篇文章主要介绍单例模式-双重锁校验,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

单例模式–双重检验锁真的线程安全吗
单例模式是我们最熟悉不过的一种设计模式,用来保证内存中只有一个对象的实例。虽然容易,但里面的坑也有很多,比如双重检验锁模式(double checked locking pattern)真的是线程安全的吗?
这里涉及到2个知识点:
1、类初始化步骤
2、指令重排
熟悉类初始化机制的同学们都应该知道类初始化分三步
1、分配对象的内存空间
2、初始化对象,调用构造方法
3、将内存地址指向instance

指令重排就是为了优化性能在不改变结果的情况下CPU按照自己的顺序去执行指令

什么是双重检验锁模式

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

我们看到,在同步代码块的内部和外部都判断了instance == null,这时因为,可能会有多个线程同时进入到同步代码块外的if判断中,如果在同步代码块内部不进行判空的话,可能会初始化多个实例。

问题所在
这种写法看似完美无缺,但它却是有问题的,或者说它并不担保一定完美无缺。主要原因在于instance = new Singleton();并不是原子性的操作。
当instance指向分配地址时,instance不为空
但是,2、3部之间,可能会被重排序,造成创建对象顺序变为1-3-2.试想一个场景:
线程A第一次创建对象Singleton,对象创建顺序为1-3-2;
当给instance分配完内存后,这时来了一个线程B调用了getSingleton()方法
这时候进行instance == null的判断,发现instance并不为null。
但注意这时候instance并没有初始化对象,线程B则会将这个未初始化完成的对象返回。那B线程使用instance时就可能会出现问题,这就是双重检查锁问题所在。

使用volatile
对于上述的问题,我们可以通过把instance声明为volatile型来解决

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

但是必须在JDK5版本以上使用。

静态内部类

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

这种写法是目前比较推荐的一种写法,采用静态内部类的方式,即实现了懒加载又不会出现线程安全问题。而且减少了synchronized的开销。

Learn more
双重检查锁定与延迟初始化
PMD-DoubleCheckedLocking
Double-checked locking: Clever, but broken

最后

以上就是文艺黑米为你收集整理的单例模式-双重锁校验的全部内容,希望文章能够帮你解决单例模式-双重锁校验所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(37)

评论列表共有 0 条评论

立即
投稿
返回
顶部