2013-04-27 1 views
31

메모리 일관성 오류에서 Java 문서를 읽는 중.Memory Consistency - Java에서 happen-before 관계

  • 성명이 Thread.start()를 호출하는 이있는 모든 문은 발생-전에 관계를 그 문으로도 이 발생-전에있는 모든과의 관계를 : 관계 전에 - 나는 일이 생성 두 가지 작업에 관련된 점을 찾을 수 새 스레드에서 실행 한 문 새 스레드를 만드는 데 도움이되는 코드 효과가 새 스레드에 표시됩니다.

  • 스레드가 종료하고 돌아 다른 스레드 에서 Thread.join()가 발생

    다음 종료
    쓰레드에 의해 실행되는 모든 문은 발생-전에 성공적으로 조인 다음 모든 문
    과의 관계가있다. 스레드에서 코드의 효과 은 이제 조인을 수행 한 스레드에서 볼 수 있습니다.

나는 그 의미를 이해할 수 없다. 누군가 간단한 예를 들어 설명하면 좋을 것입니다.

+5

'happen-before relationship'은 이러한 명령문 집합이 다른 명령문 집합보다 먼저 실행된다는 것을 의미합니다. 따라서 첫 번째 시나리오에서 ... 새 스레드 시작에 이르는 명령문은 새로 시작된 스레드에 의해 실행될 명령문과 happen-before 관계를가집니다. 이러한 명령문에 의한 모든 변경 사항은 스레드가 실행 한 명령문에서 볼 수 있습니다. –

+1

이 페이지가 도움이됩니다. http://preshing.com/20130702/the-happens-before-relation/ A와 B 사이의 "happen-before"관계가 B 이전에 실제로 발생하는 A와 어떻게 다른지 예제를 제공합니다. –

답변

26

최신 CPU는 의사 코드를 실행하는 경우와 같이 업데이트 된 순서대로 메모리에 데이터를 항상 쓰는 것은 아닙니다 (단순화를 위해 변수가 메모리에 항상 저장된다고 가정). 이 메모리에 a을 쓰기 전에

a = 1 
b = a + 1 

... CPU는 아주 잘 메모리에 b을 쓸 수 있습니다. 위의 코드를 실행하는 스레드는 할당이 이루어지면 어느 한 변수의 이전 값을 결코 볼 수 없으므로 실제로는 단일 스레드에서 작업을 실행하는 한 실제로는 문제가되지 않습니다.

멀티 쓰레딩은 또 다른 문제입니다. 다음 코드를 사용하면 다른 쓰레드가 무거운 계산의 가치를 찾게 될 것입니다.

a = heavy_computation() 
b = DONE 

... 다른 스레드 일 ... 비록 완료 플래그가 결과 이전에 메모리에 설정 될 수 있는지의 문제는 메모리에 저장된다

repeat while b != DONE 
    nothing 

result = a 

때문에 다른 스레드 계산 결과가 메모리에 기록되기 전에 메모리 주소 a의 값을 취할 수 있습니다.

같은 문제가 것 - Thread.start 경우 Thread.join가 없었다 보증 "전에 발생"- 당신에게 같은 코드에 문제를 줄; 스레드가 시작될 때

a = 1 
Thread.start newthread 
... 

newthread: 
    do_computation(a) 

... a 때문에 메모리에 저장되어있는 값을 가질 수있다.

당신은 거의 항상 새로운 스레드가 Thread.start를 호출하기 전에 업데이트되었습니다 데이터를 사용할 수 보장, 즉, Thread.start가가 보장 "하기 전에 발생"을 가지고, 당신이 그것을 시작하기 전에 초기화 된 데이터를 사용할 수 있도록 원하기 때문에 새 스레드.Thread.join에 대해서도 마찬가지입니다. 여기서 새 스레드에 의해 작성된 데이터는 종료 후 연결된 스레드에 표시 될 수 있습니다..

그냥 스레딩을 훨씬 쉽게 만듭니다.

7

스레드 가시성 문제은 Java 메모리 모델에 따라 올바르게 동기화되지 않은 코드에서 발생할 수 있습니다. 컴파일러 & 하드웨어 최적화로 인해 한 스레드의 쓰기가 다른 스레드의 읽기로 항상 표시되는 것은 아닙니다. Java 메모리 모델은 프로그래머가 스레드 가시성 문제를 피할 수 있도록 "올바르게 동기화 된"규칙을 명확하게 만드는 공식 모델입니다.

일이 끝나기 전에은 해당 모델에 정의 된 관계이며 특정 실행을 나타냅니다. 인 것으로 입증 된 쓰기 W는 이전에 읽기 R이 다른 읽기 쓰기가 없음을 가정하여 읽기가 보장됩니다 (즉, 읽기와 발생 전 관계가없는 것 또는 그 사이에 일어나는 것) 그 관계에 따라).

가장 단순한 종류의 동시 발생 관계가 동일한 스레드의 작업간에 발생합니다. 쓰레드 P의 W에서 V 로의 쓰기는 같은 쓰레드에서 V의 읽기 R 이전에 일어난다. W가 프로그램 순서에 따라 R보다 먼저 온다고 가정한다.

당신이 말하는 텍스트는 thread.start()와 thread.join()이 happen-before 관계를 보장한다는 것을 나타냅니다. thread.start()가 발생하기 전에 발생하는 모든 동작은 해당 스레드 내의 모든 동작 이전에 발생합니다. 비슷하게 thread 내의 조 치는 thread.join() 뒤에 나타나는 액션보다 먼저 발생합니다.

실제적인 의미는 무엇입니까? 예를 들어 스레드를 시작하고 스레드가 안전하지 않은 방식으로 종료 될 때까지 기다리는 경우 (예 : 오랜 시간 동안 절전 모드 또는 일부 비 동기화 된 플래그 테스트), 스레드가 수행 한 데이터 수정을 읽으려고 할 때 스레드를 사용하면 부분적으로 볼 수 있으므로 데이터 불일치가 발생할 위험이 있습니다. join() 메서드는 스레드에서 게시 한 모든 데이터가 다른 스레드에서 완전하고 일관성있게 표시되도록하는 장벽 역할을합니다. 메인 스레드가 필드 x을 변경

static int x = 0; 

public static void main(String[] args) { 
    x = 1; 
    Thread t = new Thread() { 
     public void run() { 
      int y = x; 
     }; 
    }; 
    t.start(); 
} 

:

18

이를 생각해 보자. Java 메모리 모델은 메인 스레드와 동기화되지 않은 다른 스레드가이 변경 사항을 볼 수 있다고 보장하지 않습니다. 그러나 t 스레드는 t.start()이라는 주 스레드와 t.start()x으로 변경되도록 t.run()에 표시되도록 이 1으로 지정되어 있기 때문에이 변경 사항이 표시됩니다.

같은 우려 Thread.join();

+0

글쎄, 나는 동의, critisizm을 피하기 위해 조금 대답을 변경, 지금 살펴 봐 –

1

는 오라클 문서에 따르면, 그들은 다른 특정 문에 볼 수입니다 메모리가 하나의 특정 명령문에 의해을 기록하는 그 일이-전에 관계는 단순히 보장 정의합니다.

package happen.before; 

public class HappenBeforeRelationship { 


    private static int counter = 0; 

    private static void threadPrintMessage(String msg){ 
     System.out.printf("[Thread %s] %s\n", Thread.currentThread().getName(), msg); 
    } 

    public static void main(String[] args) { 

     threadPrintMessage("Increase counter: " + ++counter); 
     Thread t = new Thread(new CounterRunnable()); 
     t.start(); 
     try { 
      t.join(); 
     } catch (InterruptedException e) { 
      threadPrintMessage("Counter is interrupted"); 
     } 
     threadPrintMessage("Finish count: " + counter); 
    } 

    private static class CounterRunnable implements Runnable { 

     @Override 
     public void run() { 
      threadPrintMessage("start count: " + counter); 
      counter++; 
      threadPrintMessage("stop count: " + counter); 
     } 

    } 
} 

출력 될 것이다

[Thread main] Increase counter: 1 
[Thread Thread-0] start count: 1 
[Thread Thread-0] stop count: 2 
[Thread main] Finish count: 2 

이보고 신호가 출력되고, 라인 [스레드의 스레드 0]으로 시작 횟수 : 1는 Thread.start의 호출 전에 모든 이의 변화()임을 나타낸다 스레드의 본문에 표시됩니다.

및 행 [스레드 메인] 완료 카운트 : 2 스레드의 본문에있는 모든 변경 사항이 Thread.join()을 호출하는 주 스레드에서 볼 수 있음을 나타냅니다.

호프가 도움이 될 것으로 기대합니다.