12

私はJavaメモリモデルのビデオプレゼンテーションを行っていますが、著者はStatic Lazy Initialization比較して使用する方が良いと言っていLazy Initializationますが、彼が何を言いたいのかはっきりとはわかりません。

コミュニティに連絡したかったので、誰かが簡単なJavaコード例Static Lazy Initializationとの違いを説明していただければ幸いです。Lazy Initialization

リファレンス:高度なプログラミングトピック-Javaメモリモデル

4

5 に答える 5

20

どちらの実装も静的である可能性があるため、これが最初の誤解です。このビデオのプレゼンターは、クラス初期化のスレッドセーフを活用する方法を説明しています。

クラスの初期化は本質的にスレッドセーフであり、クラスの初期化でオブジェクトを初期化できる場合は、オブジェクトの作成もスレッドセーフです。

これは、スレッドセーフな静的に初期化されたオブジェクトの例です。

public class MySingletonClass{

   private MySingletonClass(){

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

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

   }

}

ここで知っておくべき重要なことは、MySingletonClassインスタンス変数は、getInstance()呼び出されるまで作成または初期化されることはありません。また、クラスの初期化はスレッドセーフであるため、のinstance変数はIntiailizationOnDemandClassholder 一度安全にロードされ、すべてのスレッドに表示されます。

あなたの編集に答えるのはあなたの他のタイプの実装に依存します。ダブルチェックロックを実行する場合は、インスタンス変数を揮発性にする必要があります。DCLが必要ない場合は、変数へのアクセスを毎回同期する必要があります。次に2つの例を示します。

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;
}

public class ThreadSafeLazySingleton{
   private static ThreadSafeLazySingleton instance;

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

}

最後の例では、インスタンスのすべてのリクエストでロックを取得する必要があります。2番目の例では、アクセスごとにvolatile-readが必要です(安価な場合とそうでない場合がありますが、CPUによって異なります)。

最初の例では、CPUに関係なく、常に1回ロックされます。それだけでなく、各読み取りはスレッドセーフを心配する必要なしに正常になります。私は個人的に私がリストした最初の例が好きです。

于 2011-09-14T17:49:36.490 に答える
3

プレゼンテーションの作成者は、静的フィールドがそのフィールドを含むクラスの最初の使用時にスレッドセーフな方法で一度だけ初期化されるという事実に言及していると思います(これはJMMによって保証されています)。

class StaticLazyExample1 {

   static Helper helper = new Helper();

   static Helper getHelper() {
      return helper;
   }
}

ここでhelper、フィールドはStaticLazyExample1クラスの最初の使用時に(つまり、コンストラクターまたは静的メソッド呼び出し時に)初期化されます。

静的な遅延初期化に基づく初期化オンデマンドホルダーイディオムもあります。

class StaticLazyExample2 {

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

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

ここでは、静的メソッドHelperを最初に呼び出したときにのみインスタンスが作成されます。StaticLazyExample2.getHelper()このコードは、静的フィールドの初期化が保証されているため、スレッドセーフで正しい​​ことが保証されています。フィールドが静的初期化子に設定されている場合、そのクラスにアクセスするすべてのスレッドに正しく表示されることが保証されます。

アップデート

両方のタイプの初期化の違いは何ですか?

静的レイジー初期化は、静的フィールドの効率的なスレッドセーフなレイジー初期化を提供し、同期オーバーヘッドがゼロです。一方、非静的フィールドを遅延初期化する場合は、次のように記述します。

class LazyInitExample1 {

  private Helper instance;

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

または、Double-ChekedLockingイディオムを使用します。

class LazyInitExample2 {

    private volatile Helper helper;

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

両方とも明示的な同期が必要であり、静的な遅延初期化と比較して追加のタイミングオーバーヘッドが発生することを言及する必要がありますか?

于 2011-09-14T17:48:43.243 に答える
2

最も単純なスレッドセーフな静的レイジー初期化は、enumこれを使用することです。静的フィールドの初期化はスレッドセーフであり、クラスはとにかくレイジーロードされるため、これは機能します。

enum ThreadSafeLazyLoadedSingleton {
    INSTANCE;
}

遅延ロードされた値を使用するクラスはStringです。hashCodeは、最初に使用されたときにのみ計算されます。その後、キャッシュされたhashCodeが使用されます。

互換性がないので、一方が他方より優れているとは言えないと思います。

于 2011-09-14T22:03:55.927 に答える
1

確かに、ここでの参照は良いでしょう。どちらも同じ基本的な考え方を持っています。必要がないのに、なぜリソース(メモリ、CPU)を割り当てるのですか?代わりに、実際に必要になるまで、これらのリソースの割り当てを延期します。これは、無駄を避けるために集中的な環境では良い場合がありますが、今すぐ結果が必要で、待つことができない場合は非常に悪い場合があります。「怠惰だが慎重な」システムを追加することは非常に困難です(ダウンタイムを検出し、空き時間になるとこれらの怠惰な計算を実行するシステム)。

遅延初期化の例を次に示します。

class Lazy {

    String value;
    int computed;

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

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

}

これが静的な怠惰な初期化です

class StaticLazy {

    private StaticLazy staticLazy;
    static StaticLazy getInstance() {
        if(staticLazy == null) staticLazy = new StaticLazy();
        return staticLazy;
    }
}
于 2011-09-14T17:38:31.910 に答える
0

違いは、遅延初期化を実装するメカニズムです。Static Lazy Initializationプレゼンターとは、JVMが任意のバージョンのJavaに準拠していることに依存するこのソリューションを意味すると思います(Java言語仕様の12.4クラスとインターフェースの初期化を参照)。

Lazy Initializationおそらく、この質問に対する他の多くの回答で説明されている怠惰な初期化を意味します。このような初期化メカニズムは、Java 5までスレッドセーフではないJVMについての仮定を行います(Java 5には実際のメモリモデル仕様があるため)。

于 2011-09-14T22:22:04.883 に答える