1

읽기 about DCL in Wikipedia, DCL 및 제안 된 솔루션의 문제에 대해 궁금한 점이 있었는데, 즉 volatile 키워드가 필요한 이유는 무엇입니까? 문제는 간단히 말해서, DCL을 사용하면 부분적으로 생성 된 객체가 스레드에 의해 사용되는 경우가있을 수 있습니다. (JAVA 5+에 대한) 제안 된 솔루션 :JAVA에서 이중 확인 잠금

이제
// Works with acquire/release semantics for volatile 
// Broken under Java 1.4 and earlier semantics for volatile 
class Foo { 
    private volatile Helper helper; 
    public Helper getHelper() { 
     Helper result = helper; 
     if (result == null) { 
      synchronized(this) { 
       result = helper; 
       if (result == null) { 
        helper = result = new Helper(); 
       } 
      } 
     } 
     return result; 
    } 

    // other functions and members... 
} 

, 내 질문은 왜 도우미에서 volatile 드롭되지 않는 이유는 무엇입니까? helper = result = new Helper();에 :

result = new Helper(); 
helper = result; 

이의 시나리오 helper에서 개체가 완료 될 때까지 참조를 얻을하지 않습니다 당신은 그냥이 줄을 어기면 내가 틀렸다면 정정 해줘,하지만. 그렇지 않니? volatile의 실적은 어떻습니까?

편집 : 초기화 후 다음 줄이없는 보증을 완전히 초기화 된 개체를 않는 경우, 나는 그것을 메소드를 호출 할 수 없습니다

class Foo { 
    private volatile Helper helper; 
    public Helper getHelper() { 
     Helper result = helper; 
     if (result == null) { 
      synchronized(this) { 
       result = helper; 
       if (result == null) { 
        result = new Helper(); 
        result.someMethod(); 
        helper = result; 
       } 
      } 
     } 
     return result; 
    } 
} 

:이 코드를 가정합니다. 그럴 수있어?

+0

왜냐하면'result = new Helper(); helper = result;'는 helper = result = new Helper();와 정확히 동일합니다. 당신은 무엇이 다른 것이라고 생각 했습니까? – immibis

+4

분명히 모든 동작이 프로그램 순서와 일치하는 전체 순서로 실행된다고 가정합니다. 일반적으로 멀티 스레드 프로그램에서는 그렇지 않습니다. – nosid

+0

@nosid, 나는 초기화 라인이 초기화가 완료되기 전에 주소를받을지도 모른다는 것을 알고 있지만, 초기화가 완료된 후에 만 ​​다음 라인이 실행될 것이라고 가정했다. 그렇지 않으면 객체를 인스턴스화 한 후에 어떻게 객체를 사용할 수 있습니까? 완전히 초기화 된 적이 있다면 지금은 할 수 없습니까? EDIT를 참조하십시오. –

답변

2

volatile을 생략하면 컴파일러에서 result을 완전히 최적화 할 수 있습니다. 이 추가 로컬 변수는 그렇게 많이 변경되지 않습니다. 추가로드는 절약되지만 경쟁 조건을 수정하는 키워드는 volatile입니다.

의 우리의 코드 조각을 다음과 같은 한 가정 해 봅시다 :

그것은 다음 의사 어셈블리로 컴파일됩니다
public volatile Object helper; 

public synchronized void initHelper() { 
    Object result = new Object(); 
    helper = result; 
} 

: 당신이 volatile를 제거하면 컴파일러는 다른 스레드을지지 않습니다하도록 허용

public synchronized void initHelper() { 
    Object result = Object.new(); 
    result.<init>(); 
    // result is now fully initialized 
    // but helper is still null for all threads 
    this.helper = result; 
    // now all threads can access fully initialized helper 
} 

helper을 사용하고 코드를 재정렬하여 최적화합니다. 그것은 출력 다음과 같은 불필요한 지역 변수를 제거하고 생산하기로 결정할 수 있습니다

public synchronized void initHelper() { 
    this.helper = Object.new(); 
    // from now on other threads can access helper 
    this.helper.<init>(); 
    // but it's fully initialized only after this line 
} 

당신이 initHelper에서 함수 호출을 추가합니다. 컴파일러는 초기화 전에이 호출을 절대 두지 않습니다. 그것은 단일 스레드 에서조차도 프로그램의 의미를 바꿀 것입니다. 그러나이 필드에 액세스하는 다중 스레드 코드를 중단시키는 최적화를 수행 할 수 있습니다.

+0

감사합니다 @Banthar. 나는 아직도 조금 혼란 스럽지만 '휘발성'에 대한 더 나은 이해를 제공한다. 제발 편집을 참조하십시오. –

+0

감사합니다. @Banthar. 'volatile '키워드를 사용하지만 로컬 변수를 사용하지 않으면 어떻게됩니까? –

+0

변환이 올바르지 않습니다. 왜냐하면 ''가 예외를 throw 할 수 있기 때문입니다. – nosid