美文网首页
线程安全的延迟初始化:线程安全的创建单例

线程安全的延迟初始化:线程安全的创建单例

作者: 天冷请穿衣 | 来源:发表于2019-12-31 17:40 被阅读0次

2019-12-31

为什么要进行字段的延迟初始化?

因为对字段的延迟初始化可以降低初始化类或创建实例的开销。

不安全的双重检查加锁延迟初始化方案

public class DoubleCheckd  {
    private DoubleCheckd  instance;

    public DoubleCheckd  getInstance(){
        if(instance == null){//1
            synchronized(DoubleCheckd .class){//2
                if(instance == null){//3
                    instance = new DoubleCheckd();//4
                }
            }
        }
        return instance;//5
    }
}

这个方案是线程不安全的,问题是由于代码行4引起的。因为4实际的动作会分成下面的三个步骤:
a.在堆内存中开辟所需要的空间;
b.初始化对象;
c.将分配的空间地址赋值给引用变量。
由于为了提高代码的执行性能,编译器和系统都会进行重排序,因此上述这三个步骤不一定是按照上面的顺序执行,b和c可能被重排序,即b先于c被执行。
在单线程的情况下并不会出现任何问题的,但在多线程的情况就可能会有问题,因为当线程A刚完成步骤c但未执行b时,若恰好有线程B运行到1处,那么此时线程B就会获取到一个不为null但却未被初始化的实例对象,其后续的操作将发生不可预知的错误。
知道了造成问题的原因解决方法也就出来了。因此只有解决如下两个问题即可:
1)避免b和c的重排序
2)使c操作不对外可见

安全的延迟初始化方式

1. 基于volatile的双重检查加锁方式
public class DoubleCheckd  {
    private volatile DoubleCheckd  instance;
    public DoubleCheckd  getInstance(){
        if(instance == null){//1
            synchronized(DoubleCheckd .class){//2
                if(instance == null){//3
                    instance = new DoubleCheckd();//4
                }
            }
        }
        return instance;//5
    }
}

此优化方法很简单,就是在原来的基础上加上volatile修饰instance变量,因为根据JMM的规则,volatile能够禁止b、c的重排序。

ps:本来曾想过使用initFlag==true代替instance==null,在代码4下面加上initFlag=true以避免b和c的重排序造成的问题,但实际上也是没用的,因为initFlag=true也可能会重排序到4前面。还是会存在使用未初始化完成的对象的危险。

2. 基于类初始化的延迟方式
public class ClassInitialization {
    private static class InitialInstanceClass{
        private static ClassInitialization instance = new ClassInitialization();//1
    }

    public static ClassInitialization getInstance(){
        return InitialInstanceClass.instance;//2
    }
}

该方式是利用多线程同时加载一个类时,jvm已保障了只有一个线程能够对该类进行初始化,从而保证了初始化的安全性。也即在多线程情况下,有且只有一个线程能够执行代码1,因此即使代码执行时出现重排序也不会被其它线程看见。

若要对实例字段延迟初始化可以采用方式1,若要对静态字段延迟初始化可以采用方式2。

相关文章

  • 单例模式(Singleton)

    一、初始化单例类时即创建单例 饿汉式:(线程安全) 枚举类型:(线程安全) 二、按需、延迟创建单例 懒汉式:(线程...

  • 多线程Debug窥探单例模式

    1. 懒汉式单例模式 通过延迟初始化,降低单例创建期间的资源开销。 懒汉式单例实现,存在线程安全问题 线程任务 在...

  • 线程安全的延迟初始化:线程安全的创建单例

    2019-12-31 为什么要进行字段的延迟初始化? 因为对字段的延迟初始化可以降低初始化类或创建实例的开销。 不...

  • 单例模式

    单例模式保证全局唯一性,得考虑线程安全性延迟加载? 饿汉模式 线程安全的,但是不支持延迟加载(有问题早发现)① /...

  • 单例模式总结

    饿汉式单例: 优点:线程安全,使用时没有延迟 缺点:启动时就创建了实例,启动慢,可能造成资源浪费 懒汉式单例: ​...

  • lang3 系列之 Concurrent 包 ① 延迟初始

    lang3的LazyInitializer提供了类的延迟初始化功能,并且获取对象的时候是线程安全的,也是单例的。 ...

  • 单例模式-静态内部类实现之原理解析

    首先举个用静态内部类实现单例的例子,看代码: 用静态内部列实现单例模式,既能保证延迟加载,又能保证线程安全,只创建...

  • iOS_单例模式

    基本模式创建单例 GCD 创建单例 线程安全。 满足静态分析器的要求。 兼容了ARC

  • Java-单例模式-线程安全问题

    单例设计模式:懒汉式(延迟加载 等你需要的时候再创建对象 在多线程中会出现安全问题):解决安全问题 加了同步操作 ...

  • 单例模式的几种写法

    一、饿汉式 特性:立即创建 线程安全 没有延迟加载 二、懒汉式 特性:延迟创建,线程不安全 三、加锁的懒汉式 特性...

网友评论

      本文标题:线程安全的延迟初始化:线程安全的创建单例

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