如何解决如果在Spring Framework的@PostConstruct中初始化对象属性,是否应该将对象属性标记为volatile?
Spring框架与Java编程语言无关,它只是一个框架。因此,通常,您需要将不同线程访问的非final字段标记为volatile
。归根结底,Spring
bean只不过是Java对象,所有语言规则都适用。
final
使用Java编程语言对字段进行特殊处理。亚历山大Shipilev,在 Oracle的性能的家伙
,写了一大篇关于此事。简而言之,当构造函数初始化final
字段时,用于设置字段值的程序集会添加一个附加的内存屏障,以确保任何线程都能正确看到该字段。
对于非final
字段,不会创建此类内存屏障。因此,通常,@postconstruct
-annotated方法很可能会初始化该字段,并且另一个线程不会看到此值,或者更糟的是,在构造函数仅部分执行时看到该值。
这是否意味着您始终需要将非final
字段标记为易失性?
简而言之,是的。如果一个字段可以由不同的线程访问,则可以。不要犯与我只想了几秒钟(感谢Jk1进行更正)并考虑Java代码的执行顺序有关的错误。您可能会认为您的Spring应用程序上下文是在单个线程中引导的。这意味着引导线程将不会对非易失性字段产生问题。因此,您可能会认为一切正常,只要您不将应用程序上下文公开给另一个线程,直到它完全初始化即可,即调用带注释的方法。这样想,您可以假设,其他线程只要没有在引导后更改字段,就没有机会缓存错误的 字段值。
相反,允许编译后的代码对指令进行重新排序,即,即使@postconstruct
在相关bean暴露给Java代码中的另一个线程之前调用-
annotated方法,也不一定会在编译后的代码中保留这种 巧合之前的 关系。运行。因此,另一个线程可能 始终
读取并缓存该非volatile
字段,而该非字段甚至还没有被完全初始化,甚至被部分初始化。这可能会引入一些细微的错误,不幸的是,Spring文档并未提及此警告。JMM的这些细节是我个人更喜欢final
字段和构造函数注入的原因。
:根据另一个问题的答案,在某些情况下,未将字段标记为volatile
仍会产生有效结果。我对此进行了进一步的研究,事实上Spring框架保证了一定数量的事情发生-
开箱即用的安全性。看看JLS的事前发生关系,其中明确指出:
监视器上的解锁发生在该监视器上的每个后续锁定之前。
Spring框架利用了这一点。所有bean都存储在一个映射中,并且每次在该映射中注册或检索bean时,Spring都会获取一个特定的监视器。结果,在注册完全初始化的bean之后,同一监视器将被解锁,并在从另一个线程检索同一bean之前将其锁定。这迫使另一个线程遵守Java代码的执行顺序所反映的事前发生
关系。因此,如果您一次引导您的bean,则所有访问完全初始化的bean的线程都将看到此状态,只要它们以规范的方式访问该bean(即通过查询应用程序上下文或自动编写来进行显式检索)。例如,@postconstruct
即使不声明字段,也可以保证二传手注入或方法的使用安全volatile
。实际上,因此您应该避免使用volatile
字段,因为它们会为每次读取引入运行时开销,从而导致在循环访问字段时会感到痛苦,并且由于关键字表示错误的意图。(顺便说一句,据我所知,Akka框架采用了类似的策略,其中除Spring之外,Akka在此问题上有所保留。)
但是,此保证仅用于在引导程序之后对Bean的检索。如果volatile
在引导后更改了非字段,或者在初始化期间泄漏了bean引用,则该保证不再适用。
请查看此较早的博客条目,该条目进一步详细描述了此功能。显然,该功能并未得到记录,因为即使Spring人士也知道(但是很长时间没有做任何事情)。
解决方法
假设我在Spring单例bean @PostConstruct
(简化代码)中进行了一些初始化:
@Service
class SomeService {
public Data someData; // not final,not volatile
public SomeService() { }
@PostConstruct
public void init() {
someData = new Data(....);
}
}
我应该担心someData
其他豆的可见性并对其进行标记volatile
吗?
(假设我无法在构造函数中初始化它)
第二种情况:如果我 覆盖 in的值@PostConstruct
(例如在显式初始化或构造函数中的初始化之后),那么@PostConstruct
将不会首先写入 该属性怎么办?
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。