1

私は非常にばかげたことをしているに違いありませんが、シングルトンでオブジェクトをインスタンス化しようとすると、ExceptionInInitializerErrorが発生します。

class MySingleton {

  private static MySingleton instance = null;
  private OtherObject obj;

  // Do I instantiate obj here???
  private MySingleton() {
    //obj = new OtherObject();
  }

  // Or here?
  {
    //obj = new OtherObject();
  }

  // Or where? ...

  public static MySingleton getInstance() {
    if (instance == null)
      instance = new MySingleton();
    return instance;
  }

}

コンストラクターで他のオブジェクトを作成する必要がありますか、それともシングルトンの場合は常に空であると想定されていますか?コンストラクターとイニシャライザーブロックの両方で例外が発生します...

main()は次のとおりです。

public static void main(String[] args) {
    try {
        MySingleton singleton = MySingleton.getInstance();
    } catch (Error e) {
        System.err.println("Error!");
    }
}
4

6 に答える 6

5

怠惰な初期化のリスク(例外で現在支払っている)を排除することで、混乱の一部を回避できます。実際には静的インスタンスを返すだけなので、実行時にその静的インスタンスを作成しないのはなぜですか(つまり、nullを待たないでください)。

class MySingleton {

  private static MySingleton instance = new MySingleton();

  // You could do this here or in the constructor
  // private OtherObject obj = new OtherObject();

  /** Use this if you want to do it in the constructor instead. */
  private OtherObject obj;

  private MySingleton() {
      obj = new OtherObject();
  }

  /** Now you can just return your static reference */
  public static MySingleton getInstance() {
      return instance;
  }

}

お気づきの方もいらっしゃると思いますが、今ではすべてが決定論的で簡単です。MySingleton.instanceは実行時に入力され、静的メソッドを介してアクセスされますMySingleton.getInstance()

これは元のGOFデザインパターンブックの正確なパターンとは一致しないことはわかっていますが、クラスの使用法は事実上同じであることに気付くでしょう。

編集:他の回答やコメントで提起されたスレッドセーフポイントのいくつかをフォローアップして、質問とGOFブックで提案された元のソリューションがスレッドセーフではないことを説明しようと思います。より適切なリファレンスについては、私が所有し、ACM/Safariオンライン本棚にあるJavaConcurrencyinPracticeを参照してください。率直に言って、私のスケッチ例よりも優れた情報源ですが、ねえ、私たちは努力するしかありません...。

したがって、Thread1とThread2という名前の2つのスレッドがあり、偶然にも、それぞれが同時にMySingleton.getInstance()メソッドにヒットするとします。これは、最新のマルチコアハイパースレッドシステムで完全に可能です。現在、これら2つのスレッドがたまたまこのメソッドのエントリポイントにヒットしたとしても、どちらが特定のステートメントにヒットするかは保証されません。したがって、次のようなものが表示されます。

// Note that instance == null as we've never called the lazy getInstance() before.

Thread1: if (instance == null)
Thread2: if (instance == null) // both tests pass - DANGER
Thread1:     instance = new MySingleton();
Thread2:     instance = new MySingleton(); // Note - you just called the constructor twice
Thread1: return instance; // Two singletons were actually created 
Thread2: return instance; // Any side-effects in the constructor were called twice

if-nullテストで示されている問題は、競合状態と呼ばれます。誰がいつどのステートメントに行くのかわかりません。結果として、それは両方の糸が崖に向かって互いに競争しているようです。

両方のスレッドが同時にgetInstance()をヒットしている状態で、私の例の同じ種類の検査を見ると、次のようになります。

Thread1: return instance;
Thread2: return instance;

単純です、はい、しかしそれが私のポイントです。オブジェクトはずっと前に一度構築されたものであり、スレッドはその値が一貫していることを期待できます。人種は存在しません。

注:OtherObjectのコンストラクターの内容についてはまだ心配する必要があります。そこにある教訓:並行性は難しい。コンストラクタースレッドを安全にする場合(そうする必要があります)、OtherObjectの作成者が同じ礼儀を行っていることを確認してください。

于 2010-10-21T21:16:56.943 に答える
2

答えは簡単です。シングルトンは使用しないでください。GoFの本にはいくつかの良いアイデアがありますが、シングルトンパターンはその1つではありません。

他の人がアップスレッドと言っているように、シングルトンの適切なスレッドセーフな使用には注意が必要です。を参照してください。

スレッドセーフなシングルトンに関するIBMの記事

実際には、シングルトンが実装するものはすべてグローバルオブジェクトです。これは悪いことです、それは名前空間を汚染します。適切な単体テスト(Junitなど)は、不可能ではないにしても、はるかに困難になります。あなたはそれを必要としません。単純な静的ファクトリクラスは、よりクリーンなコードであり、地球規模の汚染を回避し、コードが少なくなります。

于 2010-10-22T02:41:12.357 に答える
1

コンストラクターでOtherObjをインスタンス化する必要があります。エラーを引き起こしている正確なコードは何ですか?

編集-以下は私のために働いた

class MySingleton {

  private static MySingleton instance = null;
  private Integer obj;


  private MySingleton() {
    obj = new Integer(2);
  }


  public static MySingleton getInstance() {
    if (instance == null)
      instance = new MySingleton();
    return instance;
  }

}

次に、メインループからgetInstanceを呼び出します。あなたは見たいかもしれません

http://en.wikipedia.org/wiki/Singleton_pattern#Java

于 2010-10-21T21:03:28.350 に答える
1
class MySingleton {

  private static MySingleton instance = null;
  private OtherObject obj;

  private MySingleton() {
      obj = new OtherObject();
  }

  public static MySingleton getInstance() {
    if (instance == null)
      instance = new MySingleton();
    return instance;
  }

}
于 2010-10-21T21:03:37.367 に答える
1

静的初期化セクションは、静的とマークされたクラスのメンバーを初期化するためのものです。OtherObject objは静的ではないため、そこで初期化しないでください。正しい場所は、コンストラクターで初期化することです。

コンストラクターにあるときにExceptionInInitializerErrorが発生する場合はobj = new OtherObject()、静的メンバーを誤って初期化している別のクラス(おそらくOtherObject?)に問題がある可能性があります。

于 2010-10-21T21:06:10.500 に答える
0

ここでは、シングルトンクラスに問題はありません。OtherObjectクラスのコンストラクターに問題があると思います。

正しい構文は次のとおりです。

class MySingleton {

  private static MySingleton instance = null;
  private OtherObject obj;

  private MySingleton() {
    obj = new OtherObject();
  }

  public static MySingleton getInstance() {
    if (instance == null)
      instance = new MySingleton();
    return instance;
  }

}

今、あなたは私たちにOtherObjectについてのより多くの情報を与える必要があります:-)

于 2010-10-21T21:37:21.480 に答える