java 延迟初始化_Java并发编程——延迟初始化占位类模式

8af8eac2eda5aa3ed317b53350551da0.png

——仅作笔记使用,内容多摘自《java并发编程实战》

在并发编程中,如果状态变量仅在单个线程中初始化和使用,自然是线程安全的,但一旦涉及到线程间的数据交互,如何声明一个用于多线程的单例状态变量才是安全的呢?最容易想到的,自然是通过一个工厂函数进行初始化并获取实例对象,如下:

public class Demo {

private static Resource resource;

public static Resource getInstance() {

if(resource == null) {

resource = new Resource();

}

return resource;

}

}

然而上述的方法存在一个典型的竞态条件,在多线程的形况下getInstance可能会返回不同的对象,导致不可预知的错误。因此需要进行同步操作:

public class Demo {

private static Resource resource;

public synchronized static Resource getInstance() {

if(resource == null) {

resource = new Resource();

}

return resource;

}

}

然而,众所周知的是,通过同步限制线程同时访问方法,会一定程度上影响程序的并发性能,于是产生了以下的初始化方法:

public class Demo {

public static Resource resource= new Resource();

public static Resource getInstance() {

return resource;

}

}

因为在初始化器中采用了特殊的方式处理静态域,并提供了额外的线程安全性保证。静态初始化器是由JVM在类的初始化阶段执行,即在类被加载后并且被线程使用之前。由于JVM在初始化期间将获得一个锁,且每个线程都至少获取一次这个锁以确保这个类已经加载,故在静态初始化期间,内存写入操作将自动对所有的线程可见,以及避免数据破坏。

简言之,类中的静态变量在声明的时候就做初始化,可以经由JVM提供线程安全方便的保证,而无需自己添加synchronized关键字去进行同步,从而减少了线程同步带来的性能消耗。这种初始化方式被称为提前初始化。相对的,之前两种初始化方式,被称为惰性初始化或者延迟初始化。

考虑到有些类的实例在初始化的时候,可能会产生比较高的开销,故人们希望在需要用到的时候再进行初始化,于是结合延迟初始化域JVM初始化静态域的特点,产生了较为常用的延迟初始化占位类模式:

public class Demo {

private static class ResourceHolder {

public static Resource resource = new Resource();

}

public static synchronized Resource getInstance() {

return ResourceHolder.resource;

}

}

因为静态类在使用的时候才会被加载,故JVM第一次加载该静态类的时候,通过JVM即可实现静态域的线程同步,即满足了延迟加载的需求,也避开了同步带来的性能消耗。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值