java 延迟初始化_Java并发编程——延迟初始化占位类模式
发布日期:2021-10-31 15:52:16 浏览次数:4 分类:技术文章

本文共 1399 字,大约阅读时间需要 4 分钟。

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即可实现静态域的线程同步,即满足了延迟加载的需求,也避开了同步带来的性能消耗。

转载地址:https://blog.csdn.net/weixin_39987847/article/details/114589733 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:java方法2数据求和_Leet Code 2 Add Two Numbers - 链表表示的两个数求和 - Java
下一篇:用java代码写美国时间_如何衡量Java代码所用的时间?

发表评论

最新留言

表示我来过!
[***.240.166.169]2024年03月28日 22时35分04秒