2

Java 言語仕様の例を自分で試してみたかったのですが、どうやらわからないことがあります。私の理解では、揮発性カウンターのインクリメントの順序は、コードに表示される順序と同じでなければならないということでした。「驚いたことに」私は、あるカウンターが他のカウンターよりも小さい、等しい、大きいという点で、ランダムなカウンター値を取得しています。私が欠けているものを説明できる人はいますか?

以下のコードと出力:

public class C {

private static volatile int i = 0;
private static volatile int j = 0;

static void one() {
    i++;
    j++;
}

static void two() {
    int a = i;
    int b = j;
    if(a < b)
        System.out.println(a + " < " + b);
    if(a > b)
        System.out.println(a + " > " + b);
    if(a == b)
        System.out.println(a + " = " + b);
}

public static void main(String[] args) throws Exception {
    Thread t1 = new Thread(new Runnable() {
        @Override
        public void run() {
            while(true)
                one();
        }
    });
    Thread t2 = new Thread(new Runnable() {
        @Override
        public void run() {
            while(true)
                two();
        }
    });

    t1.start();
    t2.start();

    Thread.sleep(5000);

    System.exit(0);
}
}

出力

214559700 > 214559699
214559807 > 214559806
214559917 > 214559916
214560019 = 214560019
214560137 > 214560136
214560247 = 214560247
214560349 > 214560348
214560455 = 214560455
214560561 > 214560560
214560670 = 214560670
214560776 = 214560776
214560886 > 214560885
214560995 = 214560995
214561097 < 214561098
4

5 に答える 5

1

i = 03 つのすべてのシナリオで、 andから始めていると想像してくださいj = 0

a == b

two() loads i (a = 0)
two() loads j (b = 0)
one() increments i (i = 1)
one() increments j (j = 1)

a > b

one() increments i (i = 1)
two() loads i (a = 1)
two() loads j (b = 0)
one() increments j (j = 1)

a < b (まれ)

two() loads i (a = 0)
one() increments i (i = 1)
one() increments j (j = 1)
two() loads j (b = 1)
于 2013-11-14T12:40:27.947 に答える
0

i と j へのアクセスは同期されていないため、次のすべてのケースが発生する可能性があります。

  • t2 は i を読み取り、t2 は j を読み取り、t1 は i を書き込み、t1 は j を書き込みます。
  • t2 は i を読み取り、t1 は i を書き込み、t2 は j を読み取り、t1 は j を書き込みます。
  • t1 は i を書き込み、t2 は i を読み取り、t2 は j を読み取り、t1 は j を書き込みます。
  • t1 は i を書き込み、t2 は i を読み取り、t2 は j を読み取り、t2 は i を読み取り、t2 は j を読み取り、t1 は j を書き込みます。
  • などなど
于 2013-11-14T12:28:29.617 に答える
0

スレッドはさまざまな方法でインターリーブできることに注意することが重要です。ijaとにコピーしているため、とへのコピーの間で、 とをb変更できます。たとえば、この種のトレースは出力を説明します。ij iajb

いくつかの初期状態、次にコピー

  • 背景: i == 214559700、j == 214559699
  • T2: a = i、b = j

    214559700 > 214559699
    

7 回インクリメントしてから、インクリメント — コピー! —メント

  • T1: (i++ j++) (x7)
  • T1: i++
  • T2: a = i、b = j

      214559807 > 214559806
    
  • T1: j++

9 回インクリメントしてから、インクリメント — コピー! —メント

  • T1: (i++ j++) (x9)
  • T1: i++
  • T2: a = i、b = j

    214559917 > 214559916
    
  • T1: j++

2回インクリメントしてからコピー

  • T1: (i++ j++) (x2)
  • T2: a = i、b = j

    214560019 = 214560019
    
于 2013-11-14T12:27:40.443 に答える
0

スレッドの 1 つがインクリメントを終了したばかりで、インクリメントiを開始したい場合j、もう 1 つのスレッドは int のaand をbすでに設定している可能性があり、結果として異なる値になります。

volatileあなたが思っているように、私は別の用途があります。基本的にvolatile、変数の値が異なるスレッドによって変更されることを示すために使用されます。volatile 修飾子は、フィールドを読み取るすべてのスレッドが最後に書き込まれた値を見ることを保証します

volatile Java 変数を宣言することは、次のことを意味します。

  • この変数の値がスレッドローカルにキャッシュされることはありません。すべての読み取りと書き込みは「メイン メモリ」に直接行われます。
  • 変数へのアクセスは、ブロックで囲まれているかのように機能し、synchronizedそれ自体で同期されます。

ソースと詳細説明。

volatile異なるメンバーではなく、異なるスレッドから同じ volatile メンバーにアクセスする場合は、 の使用がより明確になります。

于 2013-11-14T12:28:57.207 に答える