线程:延迟初始化与静态延迟初始化
我正在观看 Java 内存模型视频演示,作者说与静态延迟初始化
更好,我不清楚他想说什么。
我想接触社区,如果有人能用简单的 java 代码示例解释静态延迟初始化
和延迟初始化
之间的区别,我将不胜感激。
I am going through Java Memory Model video presentation and author is saying it is better to use Static Lazy Initialization
compared to Lazy Initialization
and I do not clear understand what he wants to say.
I wanted to reach to community and would appreciate if someone can explain difference between Static Lazy Initialization
and Lazy Initialization
with simple java code example.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(6)
两种实现都可以是静态的,因此这是第一个误解。该视频中的演示者正在解释如何利用类初始化的线程安全性。
类初始化本质上是线程安全的,如果可以在类初始化时初始化对象,则对象创建也是线程安全的。
下面是一个线程安全的静态初始化对象的示例
重要的是要知道,在调用
getInstance()
之前,MySingletonClass 实例变量永远不会被创建和/或初始化。同样,由于类初始化是线程安全的,IntiailizationOnDemandClassholder
的instance
变量将被安全加载一次,并且对所有线程可见。回答您的编辑取决于您的其他实现类型。如果你想进行双重检查锁定,你的实例变量需要是易失性的。如果您不需要 DCL,那么您将需要每次都同步对变量的访问。以下是两个示例:
和
最后一个示例需要对实例的每个请求获取锁。第二个示例需要在每次访问时进行易失性读取(可能便宜或不便宜,取决于 CPU)。
第一个示例将始终锁定一次,无论 CPU 是什么。不仅如此,每次读取都将是正常的,无需担心线程安全。我个人喜欢我列出的第一个例子。
Well both implementations can be static so that is the first misunderstanding. The presenter in this video is explaining how you can exploit the thread-safety of class initialization.
Class initialization is inherently thread-safe and if you can have an object initialized on class initialization the object creation too are thread-safe.
Here is an example of a thread-safe statically initialized object
What is important to know here, MySingletonClass instance variable will never be created and or initialized until
getInstance()
is invoked. And again since class initialization is thread-safe theinstance
variable ofIntiailizationOnDemandClassholder
will be loaded safely, once and is visible to all threads.To answer your edit depends on your other type of implementation. If you want to do double-checked-locking your instance variable would need to be volatile. If you do not want DCL then you will need to synchronize access each time to your variable. Here are the two examples:
and
The last example requires a lock acquisition on every request of the instance. The second example requires a volatile-read on each access (may be cheap or not, depends on the CPU).
The first example will always lock once regardless of the CPU. Not only that but each read will be a normal without any need to worry about thread-safety. I personally like the first example I have listed.
我认为演示文稿中的作者提到了这样一个事实:在第一次使用包含该字段的类时,静态字段只会以线程安全的方式初始化一次(这是由 JMM 保证的):
Here
helper
字段将在首次使用StaticLazyExample1
类时初始化(即在构造函数或静态方法调用时)。还有一种按需初始化持有者习惯用法,它基于静态延迟初始化:
这里是仅在首次调用
StaticLazyExample2.getHelper()
静态方法时才会创建Helper
实例。由于静态字段的初始化保证,该代码保证线程安全且正确;如果在静态初始值设定项中设置字段,则保证该字段对于访问该类的任何线程正确可见。更新
静态延迟初始化提供了静态字段的高效线程安全延迟初始化,并且同步开销为零。
另一方面,如果您想延迟初始化一个非静态字段,您应该编写如下内容:
或者使用双重检查锁定习惯用法:
我应该提到它们都需要显式同步并带有额外的与静态延迟初始化相比的时序开销?
I think the author in the presentation refers to the fact that a static field would be initialized only once in a thread-safe way at the first use of the class which contains that field (this is guaranteed by JMM):
Here
helper
field would be initialized upon first usage ofStaticLazyExample1
class (i.e. upon constructor or static method call)There is also Initialization On Demand Holder idiom, which is based on static lazy initialization:
Here a
Helper
instance would be created only upon first call toStaticLazyExample2.getHelper()
static method. This code is guaranteed to be thread-safe and correct because of the initialization guarantees for static fields; if a field is set in a static initializer, it is guaranteed to be made visible, correctly, to any thread that accesses that class.UPDATE
The static lazy initialization provides efficient thread safe lazy initialization of the static fields and has zero synchronization overhead.
On the other hand if you would like to lazily initialize a non-static field, you should write something like this:
Or use Double-Cheked Locking idiom:
Should I mention they both require explicit synchronization and carry additional timing overhead comparing to static lazy initialization?
值得注意的是,最简单的线程安全静态延迟初始化是使用枚举。这之所以有效,是因为静态字段的初始化是线程安全的,并且无论如何都会延迟加载类。
使用延迟加载值的类是 String。 hashCode 仅在第一次使用时计算。之后使用缓存的 hashCode。
我认为你不能说一个比另一个更好,因为它们实际上不能互换。
It is worth noting that the simplest thread safe static lazy initialisation is to use an
enum
This works because initialisation of static fields is thread safe and classes are lazily loaded anyway.A class which uses a lazy loaded value is String. The hashCode is only computed the first time it is used. After that the cached hashCode is used.
I don't think you can say that one is better than the other because they are not really interchangeable.
当然,这里有一个参考会很好。它们都有相同的基本思想:如果不需要,为什么要分配资源(内存、CPU)?相反,推迟这些资源的分配,直到真正需要它们为止。这在密集型环境中可以很好地避免浪费,但如果您立即需要结果并且不能等待,则可能非常糟糕。添加一个“惰性但谨慎”的系统是非常困难的(一个检测停机时间并在获得空闲时间时运行这些惰性计算的系统)。
下面是惰性初始化的示例。
这是静态延迟初始化
A reference would be good here, for sure. They both have the same basic idea: Why allocate resources (memory, cpu) if you don't have to? Instead, defer allocation of those resources until they're actually needed. This can be good in intensive environments to avoid waste, but can be very bad if you need the results right now and cannot wait. Adding a "lazy but prudent" system is very difficult (one that detects downtime and runs these lazy calculations when it gets free time.)
Here's an example of lazy initialization.
Here's static lazy initializtion
区别在于实现延迟初始化的机制。通过
静态延迟初始化
,我假设演示者的意思是这个解决方案依赖于 JVM 与任何版本的 Java 兼容(请参阅 Java 语言规范的 12.4 类和接口的初始化)。延迟初始化
可能意味着这个问题的许多其他答案中描述的延迟初始化。这种初始化机制对 JVM 做出的假设在 Java 5 之前都不是线程安全的(因为 Java 5 具有真正的内存模型规范)。The distinction is the mechanism you implement the lazy initialization. By
Static Lazy Initialization
I assume the presenter means this solution which relies on the JVM being compliant with any version of Java (see 12.4 Initialization of Classes and Interfaces, of the Java Language Specification).Lazy Initialization
probably means lazy initialization described in many other answers to this question. Such initialization mechanisms make assumptions about the JVM that are not thread-safe until Java 5 (as Java 5 has a real memory model specification).延迟加载只是一个奇特的名称,用于在实际需要时初始化类的过程。
简单来说,延迟加载是一种软件设计模式,其中对象的初始化仅在实际需要时才发生,而不是在此之前,以保持使用的简单性并提高性能。
当对象创建的成本非常高并且对象的使用非常罕见时,延迟加载就至关重要。所以这种情况下就值得实现延迟加载。延迟加载的基本思想是在需要时加载对象/数据。
资料来源: https://www.geeksforgeeks.org/lazy-loading-design-pattern /
Lazy loading is just a fancy name given to the process of initializing a class when it’s actually needed.
In simple words, Lazy loading is a software design pattern where the initialization of an object occurs only when it is actually needed and not before to preserve simplicity of usage and improve performance.
Lazy loading is essential when the cost of object creation is very high and the use of the object is very rare. So this is the scenario where it’s worth implementing lazy loading.The fundamental idea of lazy loading is to load object/data when needed.
Source: https://www.geeksforgeeks.org/lazy-loading-design-pattern/