5

Java の事前発生規則を説明するコード セグメントを含む「JVM の高度な機能とベスト プラクティスについて」を読んでいます。私は理解できない。コードは以下のとおりです。

private int value = 0;
//executed by Thread A
public void setValue(int value){
    this.value = value;
}
//executed by Thread B
public void getValue(){
    return value;
}

スレッドがコード内のAスレッドの前に開始するとBします。getValue()スレッド セーフではないため、スレッド B で返される結果がわからないことは理解できます。しかし、この本は、同期キーワードを関数setValue()getValue()に追加すると、スレッドセーフの問題は存在せず、メソッドgetValue()は正しい値を返すと述べています。この本は、なぜならそれsynchronizedは事前発生規則に適合していると説明しています。したがって、以下のコードで2つの質問があります。

public class VolatileDemo3 {
    private volatile int value = 0;
    public static void main(String[] args) {
        VolatileDemo3 v = new VolatileDemo3();
        Thread A = new Thread(v.new Test1());// Thread A
        Thread B = new Thread(v.new Test2());//Thread B
        A.start();  
        B.start();
    }
    public void setValue(int value){
        this.value  = value;
    }
    public int getValue(){
        return this.value;
    }

    public class Test1 implements Runnable {
        @Override
        public void run() {
            setValue(10);
        }   
    }
    public class Test2 implements Runnable {
        @Override
        public void run() {
            int v = getValue();
            System.out.println(v);
        }
    }
}
  1. A.start()run beforeB.start()で value は ですが、volatileスレッド B が を出力できるかどうかは保証できません10よね? スレッド B は JVM によって最初にスケジュールされる可能性があるため、スレッド B は 10 ではなく 0 を出力します。
  2. JVM によってスレッドのA前にスレッドがスケジュールされたとしても、JVM は命令を再度ソートするため、JVMによって以前に実行されBた命令を保証することはできません。私の理解は正しいですか?私を助けてください。this.value = valuereturn this.value
4

2 に答える 2

9

「前に起こった」という問題は、スレッド A がスレッド B の前に値を設定することではありません。this.value = valueスレッド B が実行される前にスレッド A が時系列的に に到達したgetValue可能性がありますが、B が見る値は依然として古い値。

つまり、スレッド化された環境では、2 つの命令が時系列で実行されたとしても、一方の結果が他方に表示されるわけではありません。

スレッド B がたまたま最初にメソッドを呼び出した場合、常に古い値が取得されます。しかし、たまたまメソッドが 2 番目に呼び出された場合、古い値を取得するか新しい値を取得するかは不明です。

このため、「前に起こる」ルールを確実にする手段を使用する必要があり、そうすれば、「前に起こった」ことの結果が「後に起こる」ことによって見られることがわかります。

たとえば、 ifvalueが揮発性である場合、setValue()スレッド B の前にスレッド A によって呼び出された場合、スレッド B は新しい値を認識します。

╔═════════════════════╤════════════════════════╤══ ═══════════════════╗
║ 操作の順序 │ 使用していますか │ どの値の║
║ │ 揮発性/同期? │Bさんは見てくれますか?║
╠═════════════════════╪════════════════════════╪══ ═══════════════════╣
║ A はsetValue(10)を実行します│ N │ 不明 ║
║ B はgetValue()を実行します   ├────────────────────────┼──────────────────── ───╢
║ │ Y │ 10 ║
╟──────────────────────┼────────────────────────┼── ────────────────────╢
║ B はgetValue()を実行します   │ N │ 0 ║
║ A はsetValue(10)を実行します├────────────────────────┼────────────────── ────╢
║ │ Y │ 0 ║
╚═════════════════════╧════════════════════════╧══ ═══════════════════╝

あなたの2つの質問について:

  1. 真実。どちらが最初にその命令にたどり着いたかはわかりません。どのスレッドが最初にスケジュールされるかだけの問題ではありません。スレッドは異なる CPU で実行されている可能性があり、1 つの CPU は長いメモリ フェッチを必要とし、もう 1 つの CPU は短いメモリ フェッチのみを必要とするため、他の CPU よりも遅くなります。また、コードを準備する機械語命令の長さが異なる場合もあります。一般に、舞台裏で何が起こっているのかはわかりません。Java は、スレッドが実行される順序について保証しません。
  2. メソッドが非常に短いため、この特定のケースで命令が再配置される可能性は低いです。繰り返しになりますが、何が起こっているのかはわかりません。これは、特定の JVM、CPU の数、CPU のタイプ、スケジューラ、およびメモリの配置に依存するためです。保証はありません。
于 2016-08-20T08:18:28.763 に答える
0

関数に synchronized を追加するsetValue/getValueことは、そのコードの一部を実行したいスレッドが最初にそのオブジェクトのロックを取得 (または待機) する必要があることを意味します。

スレッド A が setValue/getValue を呼び出す前にロックが保持されていないと仮定すると、スレッド A はすぐにロックを取得します。ただし、その間にスレッド B が を呼び出した場合setValue/getValue、メソッドを実行する前に、スレッド A がロックを解放するまで待機する必要があります。

ただし、両方のスレッドがオブジェクトのロックを待機していた場合、どちらが最初に OS によって選択されたかは保証できません。

于 2016-08-20T08:20:59.173 に答える