我正在看Java内存模型视频演示,作者说使用Static Lazy Initialization
比Lazy Initialization
更好,我不清楚他想说什么。
我想联系社区,希望有人能用简单的java代码示例解释Static Lazy Initialization
和Lazy Initialization
之间的区别。
参考:Advanced Programming Topics - Java Memory Model
我正在看Java内存模型视频演示,作者说使用Static Lazy Initialization
比Lazy Initialization
更好,我不清楚他想说什么。
我想联系社区,希望有人能用简单的java代码示例解释Static Lazy Initialization
和Lazy Initialization
之间的区别。
参考:Advanced Programming Topics - Java Memory Model
6条答案
按热度按时间7cwmlq891#
这两种实现都可能是静态的,所以这是第一个误解。本视频中的演示者将解释如何利用类初始化的线程安全性。
类初始化本质上是线程安全的,如果可以在类初始化时初始化对象,则对象创建也是线程安全的。
下面是线程安全静态初始化对象的示例
这里需要知道的是,在调用
getInstance()
之前,MySingletonClass示例变量永远不会被创建和初始化。同样,由于类初始化是线程安全的,所以IntiailizationOnDemandClassholder
的instance
变量将被安全加载一次,并且对所有线程都可见。回答您的编辑取决于您的其他类型的实现。如果你想做双重检查锁定,你的示例变量需要是易失性的。如果不需要DCL,则需要每次同步对变量的访问。以下是两个示例:
和
最后一个示例要求在示例的每个请求上获取锁。第二个示例要求在每次访问时进行易失性读取(可能便宜或不便宜,取决于CPU)。
第一个示例将始终锁定一次,而与CPU无关。不仅如此,每次读取都是正常的,无需担心线程安全。我个人喜欢我列举的第一个例子。
0pizxfdo2#
我认为演示文稿中的作者提到了这样一个事实,即在第一次使用包含该字段的类时,静态字段只会以线程安全的方式初始化一次(这由JMM保证):
这里,
helper
字段将在首次使用StaticLazyExample1
类时初始化(即在构造函数或静态方法调用时)还有基于静态惰性初始化的Initialization On Demand Holder习惯用法:
这里,
Helper
示例将仅在第一次调用StaticLazyExample2.getHelper()
静态方法时创建。由于静态字段的初始化保证,此代码保证线程安全且正确;如果在静态初始值设定项中设置了一个字段,则可以保证该字段对访问该类的任何线程都是可见的。更新
这两种类型的初始化有什么区别?
静态惰性初始化为static字段提供了高效的线程安全惰性初始化,并且同步开销为零。另一方面,如果您想懒洋洋地初始化非静态字段,您应该编写如下代码:
或者使用双重检查锁定习语:
我是否应该提到它们都需要显式同步,并且与静态延迟初始化相比会带来额外的计时开销?
n6lpvg4x3#
值得注意的是,最简单的线程安全静态惰性初始化是使用
enum
。这很有效,因为静态字段的初始化是线程安全的,并且类无论如何都是惰性加载的。使用惰性加载值的类是String。hashCode仅在首次使用时计算。之后使用缓存的hashCode。
我不认为你可以说一个比另一个好,因为它们不是真正可互换的。
yzckvree4#
当然,这里的参考资料会很好。他们都有相同的基本思想:如果不需要,为什么要分配资源(内存、cpu)?相反,将这些资源的分配推迟到实际需要时再进行。这在密集型环境中可以很好地避免浪费,但如果您现在就需要结果,并且不能等待,则可能会非常糟糕。添加一个“懒惰但谨慎”的系统是非常困难的(一个可以检测停机时间并在空闲时运行这些懒惰计算的系统)
下面是一个惰性初始化的示例。
下面是静态惰性初始化
agyaoht75#
区别在于实现惰性初始化的机制。通过
Static Lazy Initialization
,我假设演示者意味着这个解决方案,它依赖于JVM与任何版本的Java兼容(参见Java语言规范的12.4类和接口的初始化)。Lazy Initialization
可能意味着这个问题的许多其他答案中描述的惰性初始化。这种初始化机制假设JVM在Java5之前是线程安全的(因为Java5有一个真正的内存模型规范)。r8xiu3jd6#
简单地说,对象创建是根据需求完成的