2017-10-09 3 views
7

, 나는 마지막 필드에 대한 발췌를 발견최종 필드에 개체를 할당하면 다른 스레드가 해당 개체의 비 최종/비 휘발성 필드의 이전 업데이트를 볼 수 있습니까? Java 언어 사양을 읽고

마지막 필드의 사용 모델은 간단 하나입니다 : 객체의 생성자에 개체에 대한 최종 필드를 를 설정; 객체의 생성자가 완료되기 전에 다른 스레드가 그것을 볼 수있는 장소에서 생성되는 객체에 대한 참조를 쓰지 마십시오. 이 을 따르는 경우 다른 스레드에서 개체를 볼 때 스레드는 항상 개체의 최종 필드의 올바르게 구성된 버전을 볼 수 있습니다. 최종 필드가 일 때 일 때 해당 필드가 참조하는 모든 객체 버전 또는 배열도 표시됩니다.

링크 : https://docs.oracle.com/javase/specs/jls/se8/html/jls-17.html#jls-17.5

내 질문에, 않습니다 "버전"평균 업데이트입니까? 즉, 최종 필드에서 참조하는 객체의 비 최종/비 휘발성 필드는 구성 후에도 주 메모리 (로컬 캐시가 아님)에서 읽혀집니다.


는 그럼 thread #1objectB를 만들고 해당 비 최종/비 휘발성 필드 중 하나를 설정 가정 해 봅시다.

그런 다음 같은 분야는 뭔가 다른, 다음, objectB로 설정 최종 필드와 다른 objectA를 생성하는 thread #2 세트 thread #1 그것을 objectA 어딘가에 얻을 수있는 것을 넣습니다.

thread #1objectA이되고 최종 필드는 objectB으로 표시됩니다. thread #1objectB의 변경 사항을 볼 수 없습니까? thread #2으로 만드시겠습니까?

또는 여기가 무슨 뜻인지 보여주는 몇 가지 코드 :

public class Test { 
    private static final ConcurrentLinkedQueue<A> myAs = new ConcurrentLinkedQueue<>(); 
    private static long timer = System.nanoTime() + 3000000000L; // 3 seconds into the future 

    public static void main(String... args) { 
     B myB = new B("thread #1"); // Set in thread 1 

     new Thread(() -> { 
      myB.setString("thread #2"); // Set in thread 2 
      myAs.add(new A(myB)); 
     }).start(); 

     for(long i = 0; i < x; i = System.nanoTime()) {} // Busy-wait for about 3 seconds 

     System.out.println(myAs.poll().getB().getString()); // Print out value 
    } 

    public static class A { 
     private final B b; 

     public A(B b) { 
      this.b = b; 
     } 

     public B getB() { 
      return b; 
     } 
    } 

    public static class B { 
     private String s = null; 

     public B(String s) { 
      this.s = s; 
     } 

     public String getString() { 
      return s; 
     } 

     public void setString(String s) { 
      this.s = s; 
     } 
    } 
} 

코드는 업데이트 된 값을 읽을 것,하지만 그건 그냥 무작위로 운이 있는지 모르겠어요.

+3

첫 번째 질문에 아주 좋습니다. – lexicore

답변

1

"thread #1objectB의 변경 사항을 볼 수 없습니까? thread #2?"

예. thread #1 값을 캐시 할 수 있기 때문에.

최종 사양 값과 개체 게시간에 happened before이 있음을 의미합니다. (I 올바르게 예를 기억한다면)

1

다음은 예입니다 ...이 재미 있고 내가 실제로 얼마 전에 그것에 대해 읽은 생각 :

static class Holder { 

    private final String[] names; 

    static Holder newHolder; 

    public Holder() { 
     super(); 
     names = new String[3]; 
     names[0] = "first"; 
     names[1] = "last"; 
    } 

    public void newObject() { 
     newHolder = new Holder(); 
     newHolder.names[2] = "oneMore"; 
    } 

    public void readObject() { 
     System.out.println(Arrays.toString(newHolder.names)); 
    } 

} 

여기에 포함 된 두 개의 스레드가 있다고 가정 : ThreadAThreadB.그리고 ThreadAnewObject을 호출한다고 가정하십시오. 완료되면 ThreadBreadObject을 호출합니다.

ThreadBfirst, last, oneMore을 인쇄 할 것이라는 보장은 없습니다. firstlast이 확실하게 제공 될 것임을 보증합니다.

final fields used inside constructor의 경우에 사용되는 MemoryBarriers으로 생각하면이 btw를 완벽하게 이해할 수 있습니다. 현재 구현에서

실제로은 다음과 같습니다

public Holder() { 
    super(); 
    names = new String[3]; 
    names[0] = "first"; 
    names[1] = "last"; 
} 
// [StoreStore] 
// [LoadStore] 

다른 읽기 및 발생하는 저장 방지 constrcutor의 마지막에 삽입 된 두 개의 장벽이 있습니다.

관련 문제