このJavaシングルトンは、WebSphere 6で繰り返し再構築できますか?

StackOverflow https://stackoverflow.com/questions/225895

  •  03-07-2019
  •  | 
  •  

質問

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

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

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

private static Single theInstance = new Single();

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

public static Single getInstance() {
    return theInstance;
}

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

PS-Java 1.4のアプリでWebSphere 6を実行しています

役に立ちましたか?

解決

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

  

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

     

2つのクラスローダーがクラスをロードすると、   あなたは実際に2つのコピーを持っています   クラス、そしてそれぞれが独自のものを持つことができます   シングルトンインスタンス。あれは   特にサーブレットに関連   特定のサーブレットエンジンで実行   (たとえばiPlanet)、各   サーブレットはデフォルトで独自のクラスを使用します   ローダ。 2つの異なるサーブレット   共同シングルトン意志にアクセスする   実際、2つの異なるオブジェクトを取得します。

     

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

     

異なるクラスローダーによってロードされた場合、   同じ名前の2つのクラス   同じパッケージ名、として扱われます   明確な-実際にそれらが   バイト単位で同じクラス。の   異なるクラスローダーが表す   区別するさまざまな名前空間   クラス(クラスの   名前は同じです)、したがって、2    MySingleton クラスは実際には   明確です。 ("クラスローダーを   名前空間メカニズム"リソースで。)   2つのシングルトンオブジェクトは   同じ名前の2つのクラス、それは   一目で見える   同じの2つのシングルトンオブジェクト   クラス。

引用

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

他のヒント

いいえ、繰り返し構築されることはありません。これは静的なので、クラスローダーがクラスに初めて触れたときに、一度だけ構築されます。

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

GeekAndPoke から):

alt text

他の人が述べたように、静的イニシャライザーはクラスローダーごとに一度だけ実行されます。

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

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

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

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

チェックする最も明らかなことは、firstTime()を同期する必要があり、そのメソッドを終了する前にfirstTimeフラグが設定されることです。

いいえ、「シングル」の複数のコピーは作成されません。 (クラスローダーの問題は後でアクセスされます)

概要を説明した実装は、Briant Goetzの本で「Eager Initialization」と記述されています-「 Java同時実行の実践 '。

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コンテナーなど)では機能しません。

シングルトンの初期化、特にメモリモデルに関する多くの記事があります。 JSR 133 は、Javaメモリの多くの弱点に対処しました。 Java 1.5および1.6のモデル。

Java 1.5および& 1.6、いくつかの選択肢があり、それらは「 Effective Java 」という本で言及されています。ジョシュア・ブロッホ。

  1. 上記の[EJアイテム3]のような、熱心な初期化
  2. 遅延イニシャライゼーションホルダークラスイディオム[EJ Item 71]
  3. 列挙型[EJアイテム3]
  4. 「volatile」静的フィールドを使用したダブルチェックロック[EJ Item 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()'は必要ありません。

静的フィールドtheInstanceは「final」ではないことに気づいたことの1つです。 Java同時実行性の例には「final」はありませんが、EJにはあります。おそらくジェームズは、「クラスローダー」と正確性を保証するための「最終」の要件に関する彼の答えに色を追加することができます

とはいえ、「静的ファイナル」を使用すると副作用があります。 Javaコンパイラは、「静的ファイナル」を検出し、可能な限りインライン化しようとすると、非常に積極的です。これは、 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

クラスローダーの問題に関しては、答えはイエスです。複数のクラスローダーでシングルトンの複数のインスタンスを使用できます。詳細については、 wikipedia をご覧ください。 Websphere にも特定の記事があります。

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

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

EDIT :firstTime()およびdoPreparations()ビットは疑わしいように見えます。シングルトンインスタンスのコンストラクターに移動できませんか?

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

  • これはスレッドセーフではありません(インスタンスはメインメモリに「公開」されません)
  • 正しく同期されていない限り、 firstTime メソッドはおそらく複数回呼び出されます

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

また、シングルトンパターンは多くの批判を受けています。たとえば、を参照してください。シングルトン私はあなたを愛していますが、あなたは私をダウンさせています

これは、クラスがクラスローダーによってロードされるときに一度だけロードされます。 この例は、より優れたシングルトン実装を提供しますが、可能な限り遅延ロードされ、スレッドセーフです。 さらに、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()が呼び出された瞬間よりも早く参照されません(したがって、クラスローダーによって読み込まれます)。したがって、このソリューションは、特別な言語構造を必要とせずにスレッドセーフです(つまり、揮発性および/または同期)。

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

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

パッケージの日付;

import java.util.Date;

公開クラス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;
}

}

ライセンス: CC-BY-SA帰属
所属していません StackOverflow
scroll top