0

遅延初期化がスレッドセーフかどうかをテストしたいので、私のコードは次のとおりです。

package LazyInit;

import java.util.Random;

public class UnThreadSafeLazyInit {

    private ExpensiveObject instance = null;

    public ExpensiveObject getInstance() {
        if (null == instance) {
            instance = new ExpensiveObject();
        }
        System.out.println("instance=" + instance);
        return instance;
    }

    /**
     * @param args
     */
    public static void main(String[] args) {
        // TODO Auto-generated method stub


        for (int i = 0; i < 5; i++) {
            UnThreadSafeLazyInit  init = new UnThreadSafeLazyInit();
            Task t1 = init.new Task();
            Task t2 = init.new Task();
            t1.start();
            t2.start();
            try {
                Thread.sleep(4000);
            } catch (Exception e) {
                e.printStackTrace();
            }
            System.out.println(t1.getInstance() == t2.getInstance());
        }
    }

    static class ExpensiveObject {

    }

    class Task extends Thread {

        private ExpensiveObject instance = null;
        private Random rand = new Random(47);

        public void setInstance () {
            this.instance = UnThreadSafeLazyInit.this.getInstance();
        }

        public ExpensiveObject getInstance() {
            return instance;
        }

        @Override
        public void run() {
            // TODO Auto-generated method stub

            try {
                Thread.sleep(rand.nextInt(1000));
            } catch (Exception e) {
                e.printStackTrace();
            }
            setInstance();
        }
    }

}

new私のコードでは、 2 つの Thead タスクを呼び出すたびに、競合状態以降、この 2 つが ExpensiveObject への同じ参照ではない可能public ExpensiveObject getInstance()性があることを証明します。実行するinstanceと、常に で戻ります。私が知っているように、関数を使用しないと、遅延初期化に競合状態が存在するため、false が返される可能性があります。どのコードがエラーであるかを調べる必要があります。ありがとうございました。truet1.getInstance() == t1.getInstance()synchronizedpublic ExpensiveObject getInstance()

4

4 に答える 4

2

コードの検査だけでは、スレッドセーフではありません。あなたが抱えている問題は、数ミリ秒の遅延がコンピューターにとって膨大な時間であり、このタイプのテストで問題が発生する可能性が非常に低いことです。

たとえば、volatile フィールドを更新してから他のスレッドに表示されるまでの一般的な遅延は、約 5 ナノ秒です。ソリューションがスレッドセーフではないのは、このくらいの期間です。問題が発生するかどうかを確認するために、最大 1,000,000,000 ナノ秒待機します。

これは、5 秒間続く花火が打ち上げられたかどうかを確認しようとするようなものですが、目を閉じてから 317 年前に花火がなかったと結論付けているようなものです。

于 2013-05-12T08:44:20.300 に答える
0

最も簡単な方法は、ExpensiveObject を非常に高価なオブジェクトにすることです。

public class ExpensiveObject {
    public ExpensiveObject() {
        System.out.println("I'm expensive!");
        try {
            Thread.sleep(2000L);
        }
        catch (InterruptedException e) {
        }
        System.out.println("See. It took 2 seconds to create me!");
    }
}

そうしないと、rece 状態に入る可能性は非常に低くなります。特に、1 つのスレッドが別のスレッドの後に開始され、別のスレッドの後に呼び出されるためsetInstance()です。

于 2013-05-12T08:28:48.537 に答える
0

スレッドセーフではありません。今回は運が良かっただけです。コードを変更します。

public ExpensiveObject getInstance() {
    if (null == instance) {
        System.out.println("old instance=" + instance);
        instance = new ExpensiveObject();
        System.out.println("new instance=" + instance);
    }
    return instance;
}
// In main
Thread.sleep(40); // Thread.sleep(4000);
// In run
Thread.sleep(rand.nextInt(10)); // Thread.sleep(rand.nextInt(1000));

falseこのコードを使用すると、コンソールに多くのことが表示されます。

于 2013-05-12T08:57:36.613 に答える