いいえ、「Single」の複数のコピーは作成されません。( Classloader の問題は後で訪問します )
あなたが概説した実装は、Briant Goetzの本「 Java Concurrency in Practice 」で「Eager Initialization」として説明されています。
public class Single
{
private static Single theInstance = new Single();
private Single()
{
// load properties
}
public static Single getInstance()
{
return theInstance;
}
}
ただし、コードは必要ありません。インスタンスの作成後にコードが遅延初期化を実行しようとしています。これには、使用する前にすべてのクライアント ライブラリで「firstTime()/doPreparation()」を実行する必要があります。コードを非常に脆弱にする正しいことをクライアントに依存することになります。
次のようにコードを変更して、コードが重複しないようにすることができます。
public class Single
{
private static Single theInstance = new Single();
private Single()
{
// load properties
}
public static Single getInstance()
{
// check for initialization of theInstance
if ( theInstance.firstTime() )
theInstance.doPreparation();
return theInstance;
}
}
残念ながら、これは遅延初期化の貧弱な実装であり、並行環境 (J2EE コンテナーなど) では機能しません。
Singleton の初期化、特にメモリ モデルについて書かれた記事が多数あります。 JSR 133は、Java 1.5 および 1.6 の Java メモリ モデルの多くの弱点に対処しました。
Java 1.5 & 1.6 では、いくつかの選択肢があり、それらはJoshua Bloch による本「 Effective Java 」で言及されています。
- Eager Initialzation、上記 [EJ Item 3] と同様
- 遅延初期化ホルダー クラス イディオム [EJ アイテム 71]
- 列挙型 [EJ 項目 3]
- 「揮発性」静的フィールドによる二重チェック ロック [EJ アイテム 71]
解決策 3 と 4 は、Java 1.5 以降でのみ機能します。したがって、最良の解決策は#2です。
これが疑似実装です。
public class Single
{
private static class SingleHolder
{
public static Single theInstance = new Single();
}
private Single()
{
// load properties
doPreparation();
}
public static Single getInstance()
{
return SingleHolder.theInstance;
}
}
「doPreparation()」はコンストラクターの内部にあるため、適切に初期化されたインスタンスを取得することが保証されていることに注意してください。また、JVM の遅延クラス ローディングを利用しているため、同期「getInstance()」は必要ありません。
static フィールド theInstance が「final」ではないことに気付きました。 Java Concurrency の例には「final」がありませんが、EJ にはあります。おそらくジェームズは、「クラスローダー」と「最終」の要件に関する彼の回答に色を追加して、正確さを保証できるでしょう。
そうは言っても、「静的最終」を使用すると副作用があります。Java コンパイラは、'static final' を検出すると非常に攻撃的になり、可能な限りインライン化を試みます。これは、Jeremy Manson によるブログ投稿で言及されています。
簡単な例を次に示します。
ファイル: A.java
public class A
{
final static String word = "Hello World";
}
ファイル: B.java
public class B
{
public static void main(String[] args) {
System.out.println(A.word);
}
}
A.java と B.java の両方をコンパイルした後、A.java を次のように変更します。
ファイル: A.java
public class A
{
final static String word = "Goodbye World";
}
「A.java」を再コンパイルし、B.class を再実行します。得られる出力は
Hello World
クラスローダーの問題については、答えはイエスです。複数のクラスローダーでシングルトンのインスタンスを複数持つことができます。詳細については、ウィキペディアを参照してください。Websphereに関する特定の記事もあります。