gpt4 book ai didi

java - 如果我在 Spring Framework 的 @PostConstruct 中初始化对象属性,我应该将它们标记为 volatile 吗?

转载 作者:IT老高 更新时间:2023-10-28 13:52:37 25 4
gpt4 key购买 nike

假设我在 Spring 单例 bean 中做了一些初始化 @PostConstruct (简化代码):

@Service
class SomeService {
public Data someData; // not final, not volatile

public SomeService() { }

@PostConstruct
public void init() {
someData = new Data(....);
}
}

我应该担心 someData其他 bean 的可见性并标记它 volatile ?

(假设我不能在构造函数中初始化它)

第二种情况:如果我 覆盖 值在 @PostConstruct (例如在显式初始化或构造函数初始化之后),所以写在 @PostConstruct 不会先写到这个属性?

最佳答案

Spring 框架与 Java 编程语言无关,它只是一个框架。因此,一般情况下,您需要mark a non-final field that is accessed by different threads成为 volatile .归根结底,Spring bean 只不过是一个 Java 对象,所有语言规则都适用。
final字段在 Java 编程语言中受到特殊处理。 Alexander Shipilev,Oracle 性能专家,wrote a great article在这个问题上。简而言之,当构造函数初始化 final 时字段,用于设置字段值的程序集添加了一个额外的内存屏障,以确保任何线程都能正确看到该字段。

对于非 final领域,没有创建这样的内存屏障。因此,一般来说,@PostConstruct 完全有可能-annotated 方法初始化该字段,并且该值不会被另一个线程看到,或者更糟糕的是,当构造函数还只是部分执行时会看到。

这是否意味着您总是需要标记非 final字段为 volatile?

简而言之,是的。如果一个字段可以被不同的线程访问,你可以。不要犯和我一样的错误,我只考虑了几秒钟(感谢 Jk1 的更正)并从 Java 代码的执行顺序方面考虑。您可能认为您的 Spring 应用程序上下文是在单个线程中引导的。这意味着引导线程不会有非 volatile 字段的问题。因此,您可能认为只要在应用程序上下文完全初始化(即调用带注释的方法)之前不将应用程序上下文暴露给另一个线程,一切就都正常了。像这样思考,您可以假设,只要您在此引导后不更改字段,其他线程就没有机会缓存​​错误的字段值。

相比之下,编译后的代码允许对指令重新排序,即即使 @PostConstruct -annotated 方法在相关 bean 暴露给 Java 代码中的另一个线程之前被调用,这种发生在之前的关系不一定在运行时的编译代码中保留。因此,另一个线程可能总是读取和缓存非 volatile字段,而它要么根本没有初始化,要么甚至部分初始化。这可能会引入微妙的错误,遗憾的是 Spring 文档没有提到这个警告。 JMM的这些细节是我个人更喜欢final的一个原因。字段和构造函数注入(inject)。

更新 :据this answer in another question ,有些情况下不将字段标记为 volatile仍然会产生有效的结果。我对此进行了进一步调查,并且 Spring 框架实际上保证了一定数量的开箱即用的安全性。看看 JLS on happens-before明确指出的关系:

An unlock on a monitor happens-before every subsequent lock on that monitor.



Spring 框架利用了这一点。所有 bean 都存储在单个映射中,每次从该映射注册或检索 bean 时,Spring 都会获取一个特定的监视器。结果,在注册完全初始化的 bean 之后,同一个监视器被解锁,并且在从另一个线程检索同一个 bean 之前它被锁定。这会强制该另一个线程遵守由 Java 代码的执行顺序反射(reflect)的发生在之前的关系。因此,如果你引导你的 bean 一次,所有访问完全初始化 bean 的线程都会看到这个状态,只要它们以规范的方式访问 bean(即通过查询应用程序上下文或自动编写的显式检索)。这使得例如 setter 注入(inject)或使用 @PostConstruct即使不声明字段,方法也是安全的 volatile .事实上,因此您应该避免 volatile字段,因为它们为每次读取引入了运行时开销,在循环中访问字段时会变得很痛苦,并且因为关键字表示错误的意图。 (顺便说一下,据我所知,Akka 框架应用了一个类似的策略,其中 Akka 除了 Spring, drops some lines on the 问题。)

但是,此保证仅适用于在 bootstrap 之后检索 bean。如果更改非 volatile引导后的字段,或者如果您在初始化期间泄漏 bean 引用,则此保证不再适用。

退房 this older blog entry它更详细地描述了这个特性。显然,此功能甚至没有记录为 the Spring people are aware of (但很长时间没有做任何事情)。

关于java - 如果我在 Spring Framework 的 @PostConstruct 中初始化对象属性,我应该将它们标记为 volatile 吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/23906808/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com