Java静的初期化子はスレッドセーフですか?


136

静的コードブロックを使用して、レジストリ内のいくつかのコントローラーを初期化しています。したがって、私の質問は、クラスが最初にロードされたときに、この静的コードブロックが絶対に一度だけ呼び出されることを保証できますか?このコードブロックがいつ呼び出されるかは保証できないことを理解しています。Classloaderが最初にそれをロードしたときに、それを推測しています。静的コードブロックのクラスで同期できることはわかっていますが、これは実際に何が起きているのでしょうか。

簡単なコード例は次のようになります。

class FooRegistry {

    static {
        //this code must only ever be called once 
        addController(new FooControllerImpl());
    }

    private static void addController(IFooController controller) { 
        // ...
    }
}

または私はこれを行うべきですか?

class FooRegistry {

    static {
        synchronized(FooRegistry.class) {
            addController(new FooControllerImpl());
        }
    }

    private static void addController(IFooController controller) {
        // ...
    }
}

10
テストできないので、このデザインは好きではありません。Dependency Injectionをご覧ください。
dfa 2009年

回答:


199

はい、Java静的初期化子はスレッドセーフです(最初のオプションを使用してください)。

ただし、コードが1回だけ実行されるようにしたい場合は、クラスが単一のクラスローダーによってのみロードされるようにする必要があります。静的初期化は、クラスローダーごとに1回実行されます。


2
ただし、クラスは複数のクラスローダーによってロードできるため、addControllerは引き続き複数回呼び出される可能性があります(呼び出しを同期するかどうかに関係なく)...
Matthew Murdoch

4
しばらく待ってください。つまり、クラスをロードするすべてのクラスローダーに対して静的コードブロックが実際に呼び出されるということです。うーん...私は、これはまだ大丈夫、しかし、イム.. OSGIのENVにこのコードの並べ替えを実行するとmulitpleバンドルクラスローダで、どのように動作するか不思議に思われる必要がありますね
simon622

1
はい。静的コードブロックは、クラスをロードするすべてのクラスローダーに対して呼び出されます。
マシューマードック

3
@ simon622はい。ただし、各ClassLoaderの異なるクラスオブジェクトで動作します。完全修飾名は同じであるが、互いにキャストできない異なるタイプを表す、異なるClassオブジェクト。
Erwin Bolwidt 16年

1
これは、「final」キーワードがインスタンスホルダーで冗長であることを意味します。en.wikipedia.org / wiki / Initialization-on-demand_holder_idiom
spc16670 2016年

11

これは遅延初期化に使用できるトリックです

enum Singleton {
    INSTANCE;
}

またはJava 5.0より前の場合

class Singleton {
   static class SingletonHolder {
      static final Singleton INSTANCE = new Singleton();
   }
   public static Singleton instance() {
      return SingletonHolder.INSTANCE;
   }
}

SingletonHolderの静的ブロックはスレッドセーフな方法で1回実行されるため、他のロックは必要ありません。SingletonHolderクラスは、instance()を呼び出したときにのみロードされます


18
この答えは、静的ブロックがグローバルに1回だけ実行されるという事実に基づいています。これは、まさに質問された質問です。
マイケルマイヤーズ

2
これもマルチクラスローダー環境では安全ではないと思います。
2012

2
@Ahmadマルチクラスローダー環境は、各アプリケーションが独自のシングルトンを持つことができるように設計されています。
Peter Lawrey 2013

4

通常の状況では、静的イニシャライザのすべてがそのクラスを使用するすべての前に発生するため、通常、同期は必要ありません。ただし、クラスは、静的初期化子が呼び出すすべてのものにアクセスできます(他の静的初期化子が呼び出されるようにすることを含む)。

クラスは、ロードされたクラスによってロードできますが、すぐに初期化する必要はありません。もちろん、クラスはクラスローダーの複数のインスタンスによってロードされ、それによって同じ名前を持つ複数のクラスになることができます。


3

はい、ちょっと

static初期化子はだけなので、その定義ものスレッドセーフで、一度呼び出されます-あなたは、2つの以上の呼び出し必要があると思いstaticさえ取得スレッドの競合に初期化子を。

そうは言っても、staticイニシャライザは他の多くの点で混乱しています。実際には、それらが呼び出される特定の順序はありません。staticイニシャライザが互いに依存する2つのクラスがある場合、これは本当に混乱します。また、クラスを使用しても、static初期化子がセットアップするものを使用しない場合、クラスローダーが静的初期化子を呼び出すことは保証されません。

最後に、同期しているオブジェクトに注意してください。これは本当にあなたが求めていることではないことを理解していますが、addController()スレッドセーフにする必要があるかどうかを質問が実際に尋ねていないことを確認してください。


5
それらが呼ばれる非常に定義された順序があります:ソースコードの順序で。
mafu 2009年

また、結果を使用するかどうかに関係なく、常に呼び出されます。Java 6で変更されていない限り
mafu 2009年

8
クラス内では、イニシャライザはコードに従います。2つ以上のクラスがある場合、どのクラスが最初に初期化されるか、あるクラスが別のクラスの開始前に100%初期化されるかどうか、またはどのように「インターリーブ」されるかは定義されていません。たとえば、2つのクラスのそれぞれが互いに参照する静的イニシャライザを持っている場合、物事は醜く速くなります。イニシャライザを呼び出さずに静的final intを別のクラスに参照できる方法があると思いましたが、私は
Matt

それは醜くなります、そして私はそれを避けます。しかし、サイクルを解決する方法には明確な方法があります。「Javaプログラミング言語第4版」の引用:ページ:75、セクション:2.5.3。静的初期化:「サイクルが発生した場合、Xの静的初期化子は、Yのメソッドが呼び出されたポイントまでのみ実行されます。YがXメソッドを呼び出すと、そのメソッドは、まだ実行されていない残りの静的初期化子で実行されます。 "
JMIマディソン2018

0

はい、静的イニシャライザは1回だけ実行されます。 詳細については、こちらをお読みください


2
いいえ、複数回実行できます。
限定的な贖罪

5
いいえ、クラスローダーごとに1回実行することはできません。
ruurd

基本的な答え:静的初期化は1回だけ実行されます。高度な回答:静的initはクラスローダーごとに1回実行されます。フレージングは​​これら2つの答えを混合するため、最初のコメントは混乱を招きます。
JMIマディソン2018

-4

したがって、基本的には、シングルトンインスタンスが必要なため、多かれ少なかれ旧式の方法で実行し、シングルトンオブジェクトが1回だけ初期化されるようにする必要があります。

弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.