2009-12-17 4 views
18

Brian Goetz의 "Java Concurrency in Practice"의 코드 샘플을보고 있습니다. 그는 "ready의 값은 독자 스레드에서 결코 볼 수 없기 때문에"이 코드는 무한 루프에 머물 가능성이 있다고 말합니다. 나는"Java Concurrency in Practice"예제

public class NoVisibility { 
    private static boolean ready; 
    private static int number; 

    private static class ReaderThread extends Thread { 
     public void run() { 
      while (!ready) 
       Thread.yield(); 
      System.out.println(number); 
     } 
    } 

    public static void main(String[] args) { 
     new ReaderThread().start(); 
     number = 42; 
     ready = true; 
    } 
} 

답변

27

ready 때문에이 volatile으로 표시되지 않고는 while 루프 내에서 변경되지 않기 때문에 값이 while 루프의 시작에 캐시 할 수있다 ... 이런 일이 있는지 이해할 수 없어요 . 지터가 코드를 최적화하는 방법 중 하나입니다.

스레드가 ready = true보다 먼저 시작되고 ready = false 개의 캐시가 스레드 로컬로 읽히고 다시 읽지 않을 수도 있습니다.

the volatile keyword을 확인하십시오.

+0

'준비'가 휘발성이지만 숫자가 아닌 경우 숫자가 0으로 인쇄 될 수 있습니까? – zhiyuany

+0

안녕하세요 Qberticus, 이것은 단지 설명입니다. 주 스레드가 독자 스레드 전에 완료되면 우리는 42 번 인쇄 할 수있는 기회가 있습니까? –

8

이유는 코드 예제의 다음 섹션에서 설명합니다.

3.1.1 유효하지 않은 데이터를 충분히 동기화 프로그램이 놀라운 결과를 야기 할 수있는 방법에 보여

NoVisibility : 오래된 데이터. 리더 스레드가 ready을 검사하면 오래된 값이 표시 될 수 있습니다. 변수가에 액세스 할 때마다 동기화가 사용되지 않으면 , 해당 변수에 대한 부실 값을 볼 수 있습니다.

6

자바 메모리 모델 참조를 최적화 할 수있는 JVM이 액세스하고 필드 volatile로 표시되거나 자물쇠로 액세스가 개최되지 않는 등의 경우로는, 단일 스레드 응용 프로그램입니다 수 있습니다 (이 이야기는 조금 복잡해진다 실제로 잠금). 예에서

, 당신이 제공하는 JVM은 ready 필드는 현재 스레드 내에서 수정 될 수 있음을 추론 할 수있다, 그래서 그것은 무한 루프를 일으키는 원인이 false!ready을 대체 할 것이다. 필드를 volatile으로 표시하면 JVM에서 매번 필드 값을 확인하게됩니다 (또는 적어도 ready 변경 사항이 실행중인 스레드에 전파되는지 확인하십시오). 메모리 모델이 작동 할 수

+0

그냥 조금 궁금해서 JVM이'! ready'를'false'로 대체하는 데 부주의하지 않는 이유는 무엇입니까? 'ready'가'ReaderThread' 안에있는 필드가 아닙니다. – sleepsort

+0

휘발성으로 표시되어 있지 않으면 JVM은 Java 메모리 모델에 따라 다시 읽을 의무가 없습니다. 이 최적화 (가변 호이 스팅)는 실제로 매우 일반적입니다. – assylias

2
private static boolean ready; 
private static int number; 

방법은 각 스레드가 읽고 이러한 변수의 복사본에 기록 할 수 있다는 점이다 (문제가 너무 비 정적 멤버 변수에 영향을 미친다). 이것은 기본 아키텍처가 작동하는 방식의 결과입니다.

Jeremy Manson and Brian Goetz :

멀티 프로세서 시스템에서

프로세서 일반적으로 (데이터 프로세서에 가까이 있기 때문에) 데이터에 대한 액세스 속도와의 트래픽을 감소시켜 성능 모두를 향상 메모리 캐시의 하나 개 이상의 층을 가질 공유 메모리 버스 (많은 메모리 연산이 로컬 캐시에 의해 충족 될 수 있기 때문에) 메모리 캐시는 성능을 대폭 향상시킬 수 있지만 새로운 도전 과제를 제시합니다. 예를 들어 두 프로세서가 같은 메모리 위치를 동시에 검사 할 때 어떤 일이 발생합니까? 어떤 조건 하에서 그들은 같은 가치를 볼 것입니까?

그래서이 예에서 두 스레드는 각기 다른 캐시에서 ready의 복사본을 가진 서로 다른 프로세서에서 실행될 수 있습니다. Java 언어는 스레드가 보는 값이 동기화되는지 확인하기 위해 volatilesynchronized 메커니즘을 제공합니다.

+0

모두 감사합니다 - 지금 의미가 있습니다. 따라서 이것은 JVM이 자바 코드를 실행 가능한 코드로 매핑하는 방식에 따라 다릅니다. 비슷한 문제가 C++에서 발생할 수 있다고 가정합니까? C++에서이 문제를 처리하는 방법을 알고 있습니까? –

4

이 문제는 하드웨어에 뿌리를두고 있습니다. 각 CPU는 캐시 일관성, 메모리 가시성 및 작업 순서 변경과 관련하여 다른 동작을합니다. Java는 모든 프로그래머가 신뢰할 수있는 크로스 플랫폼 메모리 모델을 정의하기 때문에 C++보다 더 나은 모양입니다. Java 메모리 모델이 필요로하는 것보다 메모리 모델이 약한 시스템에서 Java를 실행하면 JVM이 차이점을 보완해야합니다.

C와 같은 언어는 기본 하드웨어의 메모리 모델을 "상속"합니다. C++ 프로그램을 다양한 플랫폼에서 사용할 수 있도록 공식 메모리 모델을 제공하는 작업이 진행 중입니다.

+0

이것이 진짜 브라이언 goetz인지 나는 모른다. 그러나 정말로이 점을 집으로 몰아 넣기 위해 나는 실제로 실패한 예를보고 싶다. 샘플 코드를 사용해 보았지만 많은 실행 후에도 실패 할 수는 없습니다. 어떤 충고? – jbu

2
public class NoVisibility { 

    private static boolean ready = false; 
    private static int number; 

    private static class ReaderThread extends Thread { 

     @Override 
     public void run() { 
      while (!ready) { 
       Thread.yield(); 
      } 
      System.out.println(number); 
     } 
    } 

    public static void main(String[] args) throws InterruptedException { 
     new ReaderThread().start(); 
     number = 42; 
     Thread.sleep(20000); 
     ready = true; 
    } 
} 

장소 JIT 그 20 초 동안 킥 것입니다 일어날하고 확인을 최적화하고 값을 캐시 또는 전부 조건을 제거 할 것을 20 초에 대한 Thread.sleep를()를 호출합니다. 따라서 코드는 가시성에 실패합니다.

그런 일이 발생하지 않도록하려면 반드시 volatile을 사용해야합니다.

+0

수십 번의 시도 후에 이것이 실패한 것 같지 않습니다. 자바 1.8 사용 – jbu