1

System.out.println()コードをある程度スレッドセーフにすることを主張する多くの投稿を読んだことがあります。そのため、レースをシミュレートするにSystem.out.println()は、コードから削除する必要があります。

現在、 のwrite()メソッドはストリームに書き込む前にPrintStream同期するthisため、 が呼び出されるたびwrite()にロックが保持され、解放されます。

write()PrintStreamのメソッド

public void write(int b) {
    try {
        synchronized (this) { //acquires a lock
        ensureOpen();
        out.write(b);
        if ((b == '\n') && autoFlush)
            out.flush();
        }
    }
    catch (InterruptedIOException x) {
        Thread.currentThread().interrupt();
    }
    catch (IOException x) {
        trouble = true;
    }
    }

しかし、それは人種の行動に影響を与えるでしょうか?

仮定する:

boolean flag = true;

Thread 1: 
System.out.println();
flag = false;
System.out.println();


Thread 2:
System.out.println();
while(flag){
    System.out.println();
}

ここで、両方のスレッドが同じオブジェクトthis(PrintStream)をロックしていることがわかります。同じロックを取得および解放しているflag2 つの間に が含まれているため、値はキャッシュからフラッシュされ、他のオブジェクトが存在するメモリ内で更新されます。スレッドはそれを見ることができます。sysoutsflag

レースをシミュレートするのは難しいので、このコードがスレッドセーフになり、スレッド 2 に加えられた変更が表示されるという理論的な可能性はありますflagか?

はいの場合、を使用して同じ効果をvolatile達成できSystem.out.println();ますか?

4

2 に答える 2

1

System.out.println()通常、コードに断続的な問題があり、「デバッグ」を追加すると問題が解消されると、関連するスレッドセーフな動作println()の問題が発生します。println の同期で発生するキャッシュ コヒーレント ストアにより、バグは「見えなくなります」....しかし....

...バグがなくなるわけではなく、発生する可能性がはるかに低くなるだけであり、実用的な目的では、バグはなくなります。しかし、println を削除すると、バグが再発します。

コード例では、ブール値を持つ 2 つのスレッドがあります。私があなたのコードを読んだとき、望ましい動作 (スレッドセーフにする) は、次のスレッド 2 の動作であると解釈します。

Thread 2:
System.out.println();
while(flag){
    System.out.println(); // PL2 - PrintLine2
}

コードの意図は、2 番目の println がフラグが true の場合にのみ発生するようにすることです。

つまり、ループ内で、行が (フラグ値を println に追加する) 場合:

System.out.println(flag); // PL2 - PrintLine2

印刷された結果は常に「真」である必要があり、決して偽ではありません。

これは事実のように見えますが、それはこのコードが簡単だからです.... printlnの前に何か重要な「作業」が起こっていれば、それはより明白になります....例えば、以下:

public class ThreadPrintln {

    private static boolean flag = true;

    private static class ThreadTwo implements Runnable {
        @Override
        public void run() {

            long sum = 1L;

            System.out.println("Thread2Start");
            while (flag) {
                // non-trivial code
                for (int i = 0; i < 100000; i++) {
                    sum += i;
                }
                System.out.println(flag); // PL2 - PrintLine2
            }
            System.out.println("Sum is " + sum);

        }
    }

    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(new ThreadTwo());
        t.start();
        System.out.println("Main About to wait");
        Thread.sleep(1000);
        System.out.println("Main about to set");
        flag = false;
        System.out.println("Main about to join");
        t.join();
        System.out.println("Main about to exit");
    }
}

これをラップトップで実行すると、次の結果が得られます。

true
true
true
....
.... many true values
....
true
true
true
Main about to set
Main about to join
false
Sum is 38724612750001
Main about to exit

** 最後から 2 行目に「false」が出力されていることに注意してください!!**

println() を使用しても、ロジック スレッド セーフにはなりません。

「重要な」コードが少なくなるにつれて、悪い振る舞いをする可能性は減りますが、完全になくなるわけではありません。「重要な」コードを削除しても、スレッドの誤動作の可能性がなくなるわけではありません。テスト時にそれを確認する可能性が低くなるだけです...しかし、ある時点で誤動作します。

于 2013-08-31T15:50:14.007 に答える