このJavaシングルトンは、WebSphere 6で繰り返し再構築できますか?
質問
システムの問題を追跡しようとしていますが、次のコードが心配です。プライマリサーブレットの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 から):
他の人が述べたように、静的イニシャライザーはクラスローダーごとに一度だけ実行されます。
私が見たいものの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 」という本で言及されています。ジョシュア・ブロッホ。
- 上記の[EJアイテム3]のような、熱心な初期化
- 遅延イニシャライゼーションホルダークラスイディオム[EJ Item 71]
- 列挙型[EJアイテム3]
- 「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;
}
}