栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 面试经验 > 面试问答

如果在Spring Framework的@PostConstruct中初始化对象属性,是否应该将对象属性标记为volatile?

面试问答 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

如果在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人士也知道(但是很长时间没有做任何事情)。



转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/440469.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号