4
public class Foo {

    private static  Foo foo;
    private Foo(){}
    public static Foo getInstance(){
        if (foo==null){
            try {
                Thread.sleep(1);
            } catch (InterruptedException e) {}
            foo = new Foo();
            System.out.println(foo+"----"+Thread.currentThread().getName());
        }
        return foo;
    }

    public static void main(String[] args) throws Exception {

        Thread thread1 = new Thread(new Runnable() {
            @Override
            public void run() { 
                Foo foo1 = Foo.getInstance();
            }
        },"thread1");

        Thread thread2 = new Thread(new Runnable() {
            @Override
            public void run() {
                Foo foo2 = Foo.getInstance();
            }
        },"thread2");
    thread1.start();
    thread2.start();
    }
}

これらのコードはマルチスレッド環境では安全ではないことをシミュレートしたいだけですが、常に次のような2つの同じオブジェクトを出力します:

Foo@24c21495----thread1
Foo@24c21495----thread2

また

Foo@3d4b7453----thread1
Foo@3d4b7453----thread2

...

なぜ?

4

4 に答える 4

5

ここには2つの複合的な理由があると思います:

  • 最初に印刷するのは結果です。おそらくあらゆる種類のコードがここで JIT コンパイルされ、他のリソースが初期化される可能性があります。これは、本質的な同期ポイントとして効果的に機能していると思います。
  • あなたの睡眠は非常に短く、均一です。これは役に立ちません。

基本的に、両方のスレッドが新しいインスタンスを作成し、両方が結果を表示していると思います.「運が良ければ」、両方のスレッドは、2番目に割り当てを実行したスレッドの結果を見ています.

追加するだけの場合:

System.out.println("Starting");

の最初まで、異なる出力が表示されることmainがあります。睡眠も次のように変更した場合:

Thread.sleep((long) (Math.random() * 100));

...それなら、あなたはそれを見る可能性がはるかに高くなります。基本的に、一方のスレッドが「スリープ、初期化、印刷」の全体を実行し、もう一方のスレッドがスリープしている必要があります。

もう 1 つのオプションは、単純にコンストラクターに print ステートメントを追加することです。両方のスレッドが現在の診断場所で同じオブジェクトを表示している場合でも、2 つのインスタンスが構築されていることがわかります。これは、コードが安全でないことを示しています。

于 2013-06-03T06:07:17.837 に答える
0

Because your sleep(1) only sleeps for 1 millisecond. By the time the 2nd thread hits getInstance your foo has already been initialized.

What you probably want is to call a synchronized init function which checks if foo is null before setting it. No need to sleep at all. It will still output the same, but will be a thread safe initializer.

Example:

public static Foo getInstance () {
  if( foo == null )
    initFoo();
  return foo;
}

private static synchronized void initFoo () {
  if( foo == null )
    foo = new Foo();
}
于 2013-06-03T06:07:52.627 に答える