source

!= 점검 나사산이 안전합니까?

nicesource 2023. 2. 6. 23:58
반응형

!= 점검 나사산이 안전합니까?

나는 다음과 같은 복합적인 작전이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하면 1개의 .true★★★★★★★★★★★★★★★★★★.

출력에서 알 수 있듯이 (고정되지 않은) 몇 번의 반복 후에도 출력은 일정한 값으로 나타나며 변경되지 않습니다.

출력:

일부 반복의 경우:

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

동기화가 없는 경우 이 코드

Object a;

public boolean test() {
    return a != a;
}

발생시킬 수 true은 .의test()

    ALOAD 0
    GETFIELD test/Test1.a : Ljava/lang/Object;
    ALOAD 0
    GETFIELD test/Test1.a : Ljava/lang/Object;
    IF_ACMPEQ L1
...

되어 있다a 은 두 번이면이다.a다른 스레드 비교가 생성될 수 있는 다른 스레드 비교에 의해 그 사이에 변경되었다.false

메모리 하고 있기 에, ,, 모, 모, 모, 모로 변경되는 되지 않습니다.이 문제는, 에 변경이 가해진다는 보장은 없습니다.a현재 스레드에 다른 스레드가 표시됩니다.

가 계계?????? is?a != a★★★★★★★★★★★★★★★★★?

ifa 스레드 동기화 수 No (동기가 되지 않는 경우)로 업데이트 될 수 있습니다.이치노

여러 스레드를 사용하여 프로그래밍을 시도했지만 실패하지 않았습니다.내 기계로는 인종을 시뮬레이션할 수 없을 것 같아.

★★★★★★★★★★★★★★★★★★★★★★★★★★!는 " " " " " " 의 이 " " " 입니다.a다른 스레드가 JLS에 의해 허용된 경우 코드가 스레드 세이프가 아닙니다.특정 머신 및 특정 Java 구현의 특정 테스트 케이스에서 레이스 조건을 발생시킬 수 없다고 해서 다른 상황에서 레이스 조건이 발생하는 것을 막을 수 없습니다.

은 !가 != a를 반환할 수 것을 합니까?true.

네, 이론적으로는, 특정한 상황에서요.

,,a != a할 수 있다falsea동시에 변화하고 있었다.


'이상한 행동'에 대해서:

프로그램이 일부 반복 사이에 시작되면 출력 플래그 값이 표시되며, 이는 동일한 참조에서 != 검사가 실패함을 의미합니다.그러나 몇 번 반복하면 출력은 일정한 값이 false가 되어 프로그램을 장시간 실행해도 하나의 true 출력이 생성되지 않습니다.

이 "이상한" 동작은 다음 실행 시나리오와 일치합니다.

  1. 프로그램이 로드되고 JVM이 바이트 코드를 해석하기 시작합니다.(javap 출력에서 알 수 있듯이) 바이트코드는 2개의 부하를 하기 때문에 레이스 조건의 결과를 (분명히) 확인할 수 있습니다.

  2. 잠시 후 코드는 JIT 컴파일러에 의해 컴파일됩니다.에 2개의 부하가 (JIT는 2개의 부하를 가지고 ).a ( 최적화할

  3. 이제 레이스 상태는 더 이상 드러나지 않습니다. 왜냐하면 더 이상 두 개의 부하가 없기 때문입니다.

이는 모두 JLS가 Java 구현을 가능하게 하는 것과 일치합니다.


@kriss는 다음과 같이 코멘트했다.

이것은, C 또는 C++ 프로그래머가 「정의되지 않은 동작」이라고 부르는 것 같습니다(실장에 의존합니다).이번과 같은 코너 케이스에는 자바에 UB가 몇 개 있을 것 같습니다.

Java Memory Model(JLS 17.4에서 지정됨)은 한 스레드가 다른 스레드에 의해 작성된 메모리 값을 확인하는 일련의 전제 조건을 지정합니다.어떤 스레드가 다른 스레드에 의해 작성된 변수를 읽으려고 시도하고 이러한 전제조건이 충족되지 않으면 여러 실행이 있을 수 있습니다.그 중 일부는 (어플리케이션의 요건 관점에서) 올바르지 않을 가능성이 있습니다.즉, 가능한 일련의 행동(즉, "잘 형성된 실행"의 집합)이 정의되어 있지만, 이러한 행동 중 어떤 것이 일어날지는 말할 수 없습니다.

컴파일러는 코드의 최종 효과가 같을 경우 로드를 결합 및 정렬하고 저장(및 기타 작업)할 수 있습니다.

  • 단일 스레드에 의해 실행되는 경우
  • (메모리 모델에 따라) 올바르게 동기화하는 다른 스레드에 의해 실행되는 경우.

그러나 코드가 올바르게 동기화되지 않는 경우(따라서 "hapens before" 관계가 일련의 올바른 실행을 충분히 제한하지 않는 경우) 컴파일러는 로드와 저장을 "잘못된" 결과를 제공하는 방식으로 재정렬할 수 있습니다(그러나 이는 프로그램이 잘못되었다는 의미일 뿐입니다).

test-ng에 의해 증명됨:

public class MyTest {

  private static Integer count=1;

  @Test(threadPoolSize = 1000, invocationCount=10000)
  public void test(){
    count = new Integer(new Random().nextInt());
    Assert.assertFalse(count != count);
  }

}

만 번의 호출에서 두 번의 실패가 있습니다.아니요, 스레드 세이프가 아닙니다.

아니, 그건 아니야.비교를 위해 Java VM은 2개의 값을 스택에 배치하고 비교 명령을 실행해야 합니다(이 중 하나는 "a" 유형에 따라 다름).

Java VM은 다음과 같습니다.

  1. "a"를 두 번 읽고 각각 스택에 올린 다음 결과를 비교합니다.
  2. "a"를 한 번만 읽고 스택에 올린 후 복제("dup" 명령)하여 비교 실행
  3. 지우고, 이 .false

첫 번째 경우 다른 스레드가 두 읽기 사이의 "a" 값을 수정할 수 있습니다.

선택하는 전략은 Java 컴파일러와 Java 런타임(특히 JIT 컴파일러)에 따라 달라집니다.프로그램 실행 중에 변경될 수도 있습니다.

접근 때는 이 변수를 .volatile 장벽이라고 불리는) 또는 장벽을 합니다('반쪽 기억 장벽synchronized API들어 hgiher API)를도 있습니다.AtomicInteger주네드 아하산(Juned Ahasan)

스레드 안전에 대한 자세한 내용은 JSR 133(Java Memory Model)을 참조하십시오.

그것은 모두 Stephen C에 의해 잘 설명되었다.재미 삼아 다음 JVM 매개 변수를 사용하여 동일한 코드를 실행해 볼 수 있습니다.

-XX:InlineSmallCode=0

에 의해, 에되어 「」, 「JIT」(「7」)가 됩니다.을 사용하다trueforever (200년)

JIT를 사용하다(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   

아요 no요 。a != a스레드 세이프가 아닙니다.은 load 、 은 、 이 、 분 、 성 、 성 parts this this load load load load の 세으로 구성되어 있습니다.a , 드 ,a 하다, 하다, 하다, 하다, 하다, 하다를 반복하다!= 에 대한 수 .a 값을 a2년 전

Another factor though is whether 하지만 또 다른 요인은a로컬입니다.로컬입니다. If 한다면a다른 스레드에는 다른 스레드가 있어야 하며 안전하게 나사산을 해야 합니다.로컬인 경우 다른 스레드는 액세스 할 수 없으므로 스레드 세이프가 필요합니다.

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

should also always print 또한 항상 인쇄해야 합니다.false.

Declaring 선언하다a as ~하듯이volatile 할 수 astatic또는 인스턴스(instance)를 지정합니다. 것이 .a, 가 로딩됩니다.a다른 값으로 두 번.만약의 경우a 그래volatilea는 캐시될 수 있으며 다른 스레드에서 변경해도 캐시된 값에 영향을 주지 않습니다.

이상한 행동에 대해서:

" " " 이므로a라고 표시되어 있지 .volatile수 있다, 가치가 있을 수 있습니다.a스레드에 의해 캐시될 수 있습니다. 다.a/ †a != a 버전이기 , '캐시된 버전').flag에서 항상 「」가 되어 있습니다.false를 참조해 주세요.

간단한 읽기라도 원자적인 것은 아니다. ifalong로 표시되지 않는다.volatile(32비트 JVM †)의 경우long b = a스레드 세이프가 아닙니다.

언급URL : https://stackoverflow.com/questions/18460580/is-the-check-thread-safe

반응형