141

などの複合操作は複数のi++操作を伴うため、スレッドセーフではないことを知っています。

しかし、スレッドセーフな操作で参照をチェックしていますか?

a != a //is this thread-safe

これをプログラムして複数のスレッドを使用しようとしましたが、失敗しませんでした。私のマシンではレースをシミュレートできなかったと思います。

編集:

public class TestThreadSafety {
    private Object a = new Object();

    public static void main(String[] args) {

        final TestThreadSafety instance = new TestThreadSafety();

        Thread testingReferenceThread = new Thread(new Runnable() {

            @Override
            public void run() {
                long countOfIterations = 0L;
                while(true){
                    boolean flag = instance.a != instance.a;
                    if(flag)
                        System.out.println(countOfIterations + ":" + flag);

                    countOfIterations++;
                }
            }
        });

        Thread updatingReferenceThread = new Thread(new Runnable() {

            @Override
            public void run() {
                while(true){
                    instance.a = new Object();
                }
            }
        });

        testingReferenceThread.start();
        updatingReferenceThread.start();
    }

}

これは、スレッドセーフをテストするために使用しているプログラムです。

奇妙な行動

私のプログラムがいくつかの反復の間に開始すると、出力フラグ値を取得します。これは!=、同じ参照で参照チェックが失敗したことを意味します。しかし、いくつかの反復の後、出力は定数値falseになり、プログラムを長時間実行しても単一のtrue出力は生成されません。

出力が示唆するように、n 回 (固定されていません) 反復した後、出力は一定値のように見え、変化しません。

出力:

いくつかの反復の場合:

1494:true
1495:true
1496:true
19970:true
19972:true
19974:true
//after this there is not a single instance when the condition becomes true
4

8 に答える 8

6

これは Stephen C によって十分に説明されています。お楽しみとして、次の JVM パラメータを使用して同じコードを実行してみてください。

-XX:InlineSmallCode=0

これにより、JIT による最適化が妨げられ (hotspot 7 サーバーで行われます)、true永久に表示されます (2,000,000 で停止しましたが、その後も続くと思います)。

参考までに、以下は JIT されたコードです。正直なところ、テストが実際に行われたかどうか、または 2 つの負荷がどこから発生したかを知るのに十分なほど流暢にアセンブリを読んでいません。(26 行目はテストflag = a != aで、31 行目は の右中括弧ですwhile(true))。

  # {method} 'run' '()V' in 'javaapplication27/TestThreadSafety$1'
  0x00000000027dcc80: int3   
  0x00000000027dcc81: data32 data32 nop WORD PTR [rax+rax*1+0x0]
  0x00000000027dcc8c: data32 data32 xchg ax,ax
  0x00000000027dcc90: mov    DWORD PTR [rsp-0x6000],eax
  0x00000000027dcc97: push   rbp
  0x00000000027dcc98: sub    rsp,0x40
  0x00000000027dcc9c: mov    rbx,QWORD PTR [rdx+0x8]
  0x00000000027dcca0: mov    rbp,QWORD PTR [rdx+0x18]
  0x00000000027dcca4: mov    rcx,rdx
  0x00000000027dcca7: movabs r10,0x6e1a7680
  0x00000000027dccb1: call   r10
  0x00000000027dccb4: test   rbp,rbp
  0x00000000027dccb7: je     0x00000000027dccdd
  0x00000000027dccb9: mov    r10d,DWORD PTR [rbp+0x8]
  0x00000000027dccbd: cmp    r10d,0xefc158f4    ;   {oop('javaapplication27/TestThreadSafety$1')}
  0x00000000027dccc4: jne    0x00000000027dccf1
  0x00000000027dccc6: test   rbp,rbp
  0x00000000027dccc9: je     0x00000000027dcce1
  0x00000000027dcccb: cmp    r12d,DWORD PTR [rbp+0xc]
  0x00000000027dcccf: je     0x00000000027dcce1  ;*goto
                                                ; - javaapplication27.TestThreadSafety$1::run@62 (line 31)
  0x00000000027dccd1: add    rbx,0x1            ; OopMap{rbp=Oop off=85}
                                                ;*goto
                                                ; - javaapplication27.TestThreadSafety$1::run@62 (line 31)
  0x00000000027dccd5: test   DWORD PTR [rip+0xfffffffffdb53325],eax        # 0x0000000000330000
                                                ;*goto
                                                ; - javaapplication27.TestThreadSafety$1::run@62 (line 31)
                                                ;   {poll}
  0x00000000027dccdb: jmp    0x00000000027dccd1
  0x00000000027dccdd: xor    ebp,ebp
  0x00000000027dccdf: jmp    0x00000000027dccc6
  0x00000000027dcce1: mov    edx,0xffffff86
  0x00000000027dcce6: mov    QWORD PTR [rsp+0x20],rbx
  0x00000000027dcceb: call   0x00000000027a90a0  ; OopMap{rbp=Oop off=112}
                                                ;*aload_0
                                                ; - javaapplication27.TestThreadSafety$1::run@2 (line 26)
                                                ;   {runtime_call}
  0x00000000027dccf0: int3   
  0x00000000027dccf1: mov    edx,0xffffffad
  0x00000000027dccf6: mov    QWORD PTR [rsp+0x20],rbx
  0x00000000027dccfb: call   0x00000000027a90a0  ; OopMap{rbp=Oop off=128}
                                                ;*aload_0
                                                ; - javaapplication27.TestThreadSafety$1::run@2 (line 26)
                                                ;   {runtime_call}
  0x00000000027dcd00: int3                      ;*aload_0
                                                ; - javaapplication27.TestThreadSafety$1::run@2 (line 26)
  0x00000000027dcd01: int3   
于 2013-08-27T14:56:16.283 に答える
5

いいえ、a != aスレッドセーフではありません。aこの式は、 load 、 load aagain 、 performの 3 つの部分で構成されます!=a別のスレッドがの親で固有のロックを取得しa、2 つのロード操作の間に の値を変更する可能性があります。

ただし、別の要因aは、ローカルかどうかです。がローカルの場合a、他のスレッドはそれにアクセスできないため、スレッド セーフにする必要があります。

void method () {
    int a = 0;
    System.out.println(a != a);
}

また、常に印刷する必要がありますfalse

aasを宣言しても、if isまたは instancevolatileの問題は解決しません。問題は、スレッドが の異なる値を持っていることではなく、1 つのスレッドが異なる値で 2 回ロードされることです。実際にはケースがスレッドセーフでなくなる可能性があります。そうでない場合はキャッシュされる可能性があり、別のスレッドでの変更はキャッシュされた値に影響しません。astaticaaavolatilea

于 2013-08-27T13:12:15.057 に答える
0

単純な読み取りでさえ、アトミックではありません。32 ビット JVMでは、 if aislongおよび not as thenはスレッドセーフではありません。volatilelong b = a

于 2013-09-23T08:20:56.497 に答える