13

システムの問題を追跡しようとしていますが、次のコードで心配しています。プライマリサーブレットのdoPost()メソッドで次のことが発生します(有罪を保護するために名前が変更されています)。

...
if(Single.getInstance().firstTime()){
   doPreperations();
}
normalResponse();
...

シングルトン「シングル」は次のようになります。

private static Single theInstance = new Single();

private Single() {
...load properties...
}

public static Single getInstance() {
    return theInstance;
}

getInstance()メソッドでnullのtheInstanceをチェックする代わりに、静的初期化子を使用するように設定されている方法で、これを何度も再構築できますか?

PS-Java1.4上のアプリでWebSphere6を実行しています

4

10 に答える 10

16

Sunのサイトでこれを見つけました:

異なるクラスローダーによって同時にロードされる複数のシングルトン

2 つのクラス ローダーがクラスをロードすると、実際にはクラスのコピーが 2 つになり、それぞれが独自の Singleton インスタンスを持つことができます。これは、特定のサーブレット エンジン (iPlanet など) で実行されるサーブレットに特に関連します。この場合、各サーブレットはデフォルトで独自のクラス ローダーを使用します。ジョイント Singleton にアクセスする 2 つの異なるサーブレットは、実際には 2 つの異なるオブジェクトを取得します。

複数のクラス ローダーが発生するのは、思っているよりも一般的です。ブラウザがアプレットで使用するためにネットワークからクラスをロードするとき、ブラウザはサーバー アドレスごとに個別のクラス ローダーを使用します。同様に、Jini および RMI システムは、クラス ファイルのダウンロード元となるさまざまなコード ベースに対して個別のクラス ローダーを使用する場合があります。独自のシステムでカスタム クラス ローダーを使用している場合、すべて同じ問題が発生する可能性があります。

異なるクラスローダーによってロードされた場合、同じ名前の 2 つのクラスは、同じパッケージ名であっても、別個のものとして扱われます。実際には、バイトごとに同じクラスであってもです。異なるクラス ローダーは、(クラスの名前が同じであっても) クラスを区別する異なる名前空間を表すため、2 つの MySingletonクラスは実際には区別されます。(参考文献の「名前空間メカニズムとしてのクラス・ローダー」を参照してください。) 2 つの Singleton オブジェクトが同じ名前の 2 つのクラスに属しているため、一見すると同じクラスの 2 つの Singleton オブジェクトがあるように見えます。

引用

上記の問題に加えて、firstTime()が同期されていない場合、スレッドの問題も発生する可能性があります。

于 2008-10-22T14:14:18.987 に答える
13

いいえ、何度も構築されることはありません。これは静的であるため、Classloader がクラスに初めてアクセスしたときに一度だけ構築されます。

唯一の例外 - 複数のクラスローダーがある場合。

( GeekAndPokeから):

代替テキスト

于 2008-10-22T14:12:29.427 に答える
5

静的イニシャライザと遅延初期化の使用にまったく違いはありません。実際、同期を強制する遅延初期化を台無しにする方がはるかに簡単です。JVM は、クラスがアクセスされる前に静的初期化子が常に実行されることを保証し、それは 1 回だけ実行されます。

ただし、JVM は、クラスが 1 回だけロードされることを保証しません。ただし、複数回ロードされた場合でも、Web アプリケーション クラスローダーまたはその親のいずれかにロードされるため、Web アプリケーションには関連するシングルトンのみが表示されます。複数の Web アプリケーションをデプロイしている場合、アプリケーションごとに firstTime() が 1 回呼び出されます。

最も明白な確認事項は、firstTime() を同期する必要があることと、そのメソッドを終了する前に firstTime フラグが設定されていることです。

于 2008-10-22T14:44:20.300 に答える
5

いいえ、「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 」で言及されています。

  1. Eager Initialzation、上記 [EJ Item 3] と同様
  2. 遅延初期化ホルダー クラス イディオム [EJ アイテム 71]
  3. 列挙型 [EJ 項目 3]
  4. 「揮発性」静的フィールドによる二重チェック ロック [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に関する特定の記事もあります。

于 2008-10-22T21:37:12.993 に答える
5

他の人が述べたように、静的初期化子はクラスローダーごとに 1 回だけ実行されます。

私が見てみたいことの 1 つはメソッドです。シングルトン自体でfirstTime()作業を処理できないのはなぜですか?doPreparations()

厄介な依存関係のセットのように聞こえます。

于 2008-10-22T14:18:33.907 に答える
2

その Singleton の実装について (Singleton をまったく使用しないことを除いて) 変更する唯一のことは、インスタンス フィールドを final にすることです。静的フィールドは、クラスのロード時に一度初期化されます。クラスは遅延ロードされるため、効果的に遅延インスタンス化を無料で取得できます。

もちろん、別々のクラスローダーからロードされた場合、複数の「シングルトン」が得られますが、それは Java のすべてのシングルトンイディオムの制限です。

EDIT : firstTime() および doPreparations() ビットは疑わしく見えます。それらをシングルトン インスタンスのコンストラクタに移動することはできませんか?

于 2008-10-22T14:17:24.270 に答える
1

いいえ - の静的初期化はinstance一度だけ行われます。考慮すべき 2 つの点:

  • これはスレッド セーフではありません (インスタンスはメイン メモリに「公開」されません)。
  • 適切firstTimeに同期されていない限り、メソッドはおそらく複数回呼び出されます
于 2008-10-22T14:14:07.177 に答える
1

理論的には、一度だけ構築されます。ただし、このパターンは、「シングルトン」クラスの複数のインスタンスを取得できるさまざまなアプリケーション サーバーで壊れます (それらはスレッド セーフではないため)。

また、シングルトン パターンは多くの批判を受けてきました。たとえば、Singleton I love you を参照してください。

于 2008-10-22T14:16:18.670 に答える
0

単一のインスタンスが最終的なものであることは必須ではありません (これは、他のパターンを使用してその動作を切り替えることを避けるため、まったく良い考えではありません)。

以下のコードでは、一度だけインスタンス化される方法を確認できます (最初にコンストラクターを呼び出したとき)。

パッケージの日付;

java.util.Date をインポートします。

public class USDateFactory は DateFactory を実装します { private static USDateFactory usdatefactory = null;

private USDateFactory () { }

public static USDateFactory getUsdatefactory() {
    if(usdatefactory==null) {
        usdatefactory = new USDateFactory();
    }
    return usdatefactory;
}

public String getTextDate (Date date) {
    return null;
}

public NumericalDate getNumericalDate (Date date) {
    return null;
}

}

于 2009-06-13T15:14:30.513 に答える
0

これは、クラスローダーによってクラスがロードされるときに一度だけロードされます。この例は、より優れたシングルトン実装を提供しますが、可能な限り遅延ロードされ、スレッドセーフです。さらに、Java のすべての既知のバージョンで動作します。このソリューションは、さまざまな Java コンパイラと仮想マシン間で最も移植性があります。


public class Single {

private static class SingleHolder {
   private static final Single INSTANCE = new Single();
}

private Single() {
...load properties...
}

public static Single getInstance() {
    return SingleHolder.INSTANCE;
}

}

内部クラスは、getInstance() が呼び出された時点よりも早く参照されません (したがって、クラス ローダーによって以前にロードされません)。したがって、このソリューションは、特別な言語構造 (つまり、揮発性および/または同期化) を必要とせずにスレッドセーフです。

于 2008-10-22T14:33:37.653 に答える