2013-08-06 1 views
-1

일부 기사에서 이중 체크 잠금이 깨졌습니다. 컴파일러는 생성자의 순서를 재정렬 할 수 있기 때문에.Java에서 이중 체크 잠금 및 코드 재정렬

  1. SS는
  2. 가 다음 객체의 상태를 초기화
  3. 그럼 기준 변수 주소를 반환 객체에 메모리를 할당

일반적으로 하나는 기대되지만 :

  1. 개체에 할당 된 메모리 여야합니다.
  2. 다음 개체의 상태를 초기화
  3. 다음 참조 변수에 주소를 반환합니다.

다시 말해서 synchronized 키워드를 사용하면 JMM 사양에 따라 코드 재주문이 수행되지 않습니다.

왜 컴파일러가 synchronized() 블록 안에있을 때 생성자 이벤트의 순서를 재정렬합니까?

DCL에 관한 많은 게시물을 보았지만 JMM 및 컴파일러 순서 재 지정을 기반으로하는 설명을 기대합니다.

+2

코드 양식에서 생각하고있는 특정 이중 확인 잠금 관용구를 게시하면 도움이됩니다. DCL에는 몇 가지 변형이 있습니다. –

+2

가능한 복제본 [Java에서 이중 검사 잠금이 끊긴 이유는 무엇입니까?] (0120-555-3300) –

답변

3

컴파일러는 동기화 블록 내에 명령을 다시 정렬 할 수 있습니다. 그리고 컴파일러는 (이전에 남아있는 한) 또는 (이후에 머무르는 한)까지 이후에 다시 정렬 할 수 있습니다. 그러나 컴파일러는 이 아니며에 걸쳐에 걸쳐 자유롭게 블록 순서 (블록 시작 또는 블록 끝)를 자유롭게 재 배열 할 수 있습니다.

따라서, 구성 및 할당 된 동기 블록을 재 배열 할 수 전적으로 내에, 정확하게 동기화되지 않은 외부 뷰어 건설 전에 할당 을 볼 수있다.

+4

리오 더링, DCL (Java와 다른 언어에서 발생하는)의 다른 고전적인 문제는 다중 프로세서 시스템에서 발생합니다. 캐싱은 한 프로세서에서 수행 한 변경을 다른 프로세서에서의 변경으로 인식 할 수 있습니다. Java의 DCL 문제에 대한 자세한 설명은 http://www.cs.umd.edu/~pugh/java/memoryModel/DoubleCheckedLocking.html을 참조하십시오. –

0

왜 컴파일러는 synchronized() 블록 안에있을 때 생성자 이벤트의 순서를 재정렬합니까?

일반적으로 코드를 빠르게 실행하려면이 작업을 수행해야합니다.

Java 언어 사양 (JLS)은 구현 (예 : 컴파일러)이 인 것으로 알려 졌으므로은 특정 제약 조건에 따라 명령어 및 명령어 순서를 재정렬 할 수 있습니다.

문제는 DCL의 깨진 변형이 JLS가 말한 바깥에있는 가정을 만드는 것입니다. 결과는 JLS가 잘 형성되어 있지 않다고 말하는 실행입니다. 실제 버그/예기치 않은 동작으로 나타나는지 여부는 컴파일러 버전, 하드웨어 및 기타 여러 가지 사항에 따라 다릅니다.

그러나 요점은 컴파일러가 아무 잘못도 내지 않는다는 것입니다. DCL 코드에 오류가 있습니다.


난 그냥 JIT 컴파일러는 종종 이벤트 자체 재정렬되지 않는다는 것을 추가 할. 하드웨어 수준의 메모리 읽기/쓰기 작업에 대한 제약 조건을 제거하는 것이 일반적입니다. 예를 들어 특정 메모리 쓰기가 주 메모리로 플러시된다는 제약 조건을 제거하면 하드웨어가 느린 메모리 쓰기를 지연 (또는 전체적으로 건너 뛰기)하고 L1 캐시에 쓰기 만 할 수 있습니다. 반대로 synchronized 블록의 끝은 캐시 된 쓰기를 주 메모리에 강제로 수행하여 여분의 메모리 트래픽과 파이프 라인 정지를 초래합니다. 모든

0

첫째 : 동기화 된 키워드를 사용하는 경우

다시 코드 재주문는 결코 JMM 사양에 따라 반응이 없습니다.

위의 진술은 완전히 정확하지 않습니다. JMM은 happen-before 관계를 정의했습니다. JLS는 프로그램 순서와 발생 이전 순서 만 정의합니다. 17.4.5. Happens-before Order을 참조하십시오.

명령의 순서 변경에 영향을 미칩니다. 예를 들어,

int x = 1; 
synch(obj) { 
    y = 2; 
} 
int z = 3; 

이제 위의 코드 조각에 대해 다음 유형의 순서 재 지정이 가능합니다.

synch(obj) { 
    int x = 1; 
    y = 2; 
    int z = 3; 
} 

위의 내용은 유효한 순서입니다.

Roach Motels and The Java Memory Model을 참조하십시오.

synch(obj) { 
    int z = 3; 
    y = 2; 
    int x = 1; 
} 

위의 내용은 또한 유효한 순서 재 지정입니다.

y = 2는 잠금을 획득하고 잠금을 해제하기 전에 만 실행될 수 있습니다. 이는 JMM이 보증 한 것입니다. 또한 다른 스레드에서 올바른 효과를 보려면 동기화 된 블록 내에서만 y에 액세스해야합니다.

이제 DCL에 왔습니다.

DCL 코드를 참조하십시오.

if (singleton == null) 
    synch(obj) { 
     if(singleton == null) { 
      singleton == new Singleton() 
     } 
    } 
return singleton; 

지금 위의 접근 방식의 문제는 :

  1. singleton = new Singleton()는 단일 명령어가 아닙니다. 그러나 일련의 지시들. 생성자를 완전히 초기화하기 전에 먼저 Singleton 참조에 객체 참조가 할당 될 가능성이 큽니다.

  2. 따라서 이 발생하면 다른 스레드가 null이 아닌 것으로 간주하여 부분적으로 구성된 객체를 보는 것이 가능합니다.

위의 효과는 단일성을 휘발성으로 만들어 제어 할 수 있으며 이는 또한 보증 및 가시성 이전에 발생합니다.