java 线程:延迟初始化与静态延迟初始化

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/7420504/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-30 19:54:17  来源:igfitidea点击:

Threading : Lazy Initialization vs Static Lazy Initialization

javamultithreadingstaticlazy-initialization

提问by Rachel

I am going through Java Memory Model video presentation and author is saying it is better to use Static Lazy Initializationcompared to Lazy Initializationand I do not clear understand what he wants to say.

我正在观看 Java 内存模型视频演示,作者说Static Lazy Initialization相比之下它更好用Lazy Initialization,我不清楚他想说什么。

I wanted to reach to community and would appreciate if someone can explain difference between Static Lazy Initializationand Lazy Initializationwith simple java code example.

我想达到的社区,并希望如果有人可以解释之间的差异Static Lazy Initialization,并Lazy Initialization用简单的Java代码示例。

Reference: Advanced Programming Topics - Java Memory Model

参考:高级编程主题 - Java 内存模型

回答by John Vint

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

下面是一个线程安全的静态初始化对象的例子

public class MySingletonClass{

   private MySingletonClass(){

   }
   public static MySingletonClass getInstance(){
         return IntiailizationOnDemandClassholder.instance;
   }

   private static class IntiailizationOnDemandClassHolder{
         private static final MySingletonClass instance = new MySingletonClass();

   }

}

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 the instancevariable of IntiailizationOnDemandClassholderwill be loaded safely, once and is visible to all threads.

重要的是要知道, MySingletonClass 实例变量永远不会被创建和/或初始化,直到getInstance()被调用。再次由于类初始化是线程安全的,instance变量IntiailizationOnDemandClassholder将被安全加载,一次并且对所有线程可见。

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:

回答您的编辑取决于您的其他类型的实现。如果您想进行双重检查锁定,您的实例变量需要是可变的。如果您不想要 DCL,那么您每次都需要同步访问您的变量。下面是两个例子:

public class DCLLazySingleton{
  private static volatile DCLLazySingleton instance;

  public static DCLLazySingleton getInstace(){
     if(instance == null){
        synchronized(DCLLazySingleton.class){
            if(instance == null)
                instance=new DCLLazySingleton();
        }
     } 
     return instance;
}

and

public class ThreadSafeLazySingleton{
   private static ThreadSafeLazySingleton instance;

  public static ThreadSafeLazySingleton getInstance(){
     synchronized(ThreadSafeLazySingleton.class){
        if(instance == null){
            instance = new ThreadSafeLazySingleton();
        }
        return instance;
     } 

}

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).

最后一个示例需要对实例的每个请求进行锁定获取。第二个示例需要对每次访问进行 volatile-read(可能便宜与否,取决于 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.

无论 CPU 是什么,第一个示例将始终锁定一次。不仅如此,每次读取都将是正常的,无需担心线程安全。我个人喜欢我列出的第一个例子。

回答by corsiKa

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.)

当然,参考在这里会很好。他们都有相同的基本思想:如果不需要,为什么要分配资源(内存、cpu)?相反,将这些资源的分配推迟到实际需要时。这在密集环境中可以避免浪费,但如果您现在需要结果并且迫不及待,则可能非常糟糕。添加一个“懒惰但谨慎”的系统非常困难(一个检测停机时间并在空闲时间运行这些懒惰计算的系统。)

Here's an example of lazy initialization.

这是延迟初始化的示例。

class Lazy {

    String value;
    int computed;

    Lazy(String s) { this.value = s; }

    int compute() {
        if(computed == 0) computed = value.length();
        return computed;
    }

}

Here's static lazy initializtion

这是静态延迟初始化

class StaticLazy {

    private StaticLazy staticLazy;
    static StaticLazy getInstance() {
        if(staticLazy == null) staticLazy = new StaticLazy();
        return staticLazy;
    }
}

回答by Idolon

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):

我认为演示文稿中的作者指的是这样一个事实,即在第一次使用包含该字段的类时,静态字段将以线程安全的方式仅初始化一次(这由 JMM 保证):

class StaticLazyExample1 {

   static Helper helper = new Helper();

   static Helper getHelper() {
      return helper;
   }
}

Here helperfield would be initialized upon first usage of StaticLazyExample1class (i.e. upon constructor or static method call)

这里的helper字段将在第一次使用StaticLazyExample1类时初始化(即在构造函数或静态方法调用时)

There is also Initialization On Demand Holder idiom, which is based on static lazy initialization:

还有 Initialization On Demand Holder 习惯用法,它基于静态延迟初始化:

class StaticLazyExample2 {

  private static class LazyHolder {
    public static Helper instance = new Helper();
  }

  public static Helper getHelper() {
    return LazyHolder.instance;
  }
}

Here a Helperinstance would be created only upon first call to StaticLazyExample2.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.

这里Helper只会在第一次调用StaticLazyExample2.getHelper()静态方法时创建一个实例。由于静态字段的初始化保证,此代码保证是线程安全且正确的;如果在静态初始值设定项中设置了一个字段,则保证它对访问该类的任何线程都正确可见。

UPDATE

更新

What is the difference between both types of initialization?

两种类型的初始化有什么区别?

The static lazy initialization provides efficient thread safe lazy initialization of the staticfields and has zero synchronization overhead. On the other hand if you would like to lazily initialize a non-staticfield, you should write something like this:

静态延迟初始化提供了静态字段的高效线程安全延迟初始化,并且具有零同步开销。另一方面,如果你想延迟初始化一个非静态字段,你应该这样写:

class LazyInitExample1 {

  private Helper instance;

  public synchronized Helper getHelper() {
    if (instance == null) instance == new Helper();
    return instance;
  }
}

Or use Double-Cheked Locking idiom:

或者使用 Double-Chked Locking 成语:

class LazyInitExample2 {

    private volatile Helper helper;

    public Helper getHelper() {
      if (helper == null) {
          synchronized (this) {
              if (helper == null) helper = new Helper();
          }
      }
      return helper;
    }
}

Should I mention they both require explicit synchronization and carry additional timing overhead comparing to static lazy initialization?

我应该提一下,与静态延迟初始化相比,它们都需要显式同步并带有额外的计时开销吗?

回答by Peter Lawrey

It is worth noting that the simplest thread safe static lazy initialisation is to use an enumThis works because initialisation of static fields is thread safe and classes are lazily loaded anyway.

值得注意的是,最简单的线程安全静态延迟初始化是使用 anenum这个方法有效,因为静态字段的初始化是线程安全的,并且类无论如何都是延迟加载的。

enum ThreadSafeLazyLoadedSingleton {
    INSTANCE;
}

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.

使用延迟加载值的类是 String。hashCode 仅在第一次使用时计算。之后使用缓存的 hashCode。

I don't think you can say that one is better than the other because they are not really interchangeable.

我不认为你可以说一个比另一个更好,因为它们不能真正互换。

回答by MSN

The distinction is the mechanism you implement the lazy initialization. By Static Lazy InitializationI assume the presenter means this solutionwhich relies on the JVM being compliant with any version of Java (see 12.4 Initialization of Classes and Interfaces, of the Java Language Specification).

区别在于您实现延迟初始化的机制。通过Static Lazy Initialization我假定演示装置此溶液,其依赖于正在JVM符合Java的任何版本(见12.4初始化类的和接口,Java语言规范的)。

Lazy Initializationprobably 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).

Lazy Initialization可能意味着在这个问题的许多其他答案中描述的延迟初始化。此类初始化机制对 JVM 做出假设,直到 Java 5 才成为线程安全的(因为 Java 5 具有真实内存模型规范)。