37

자바에서 몇 가지 예를 보았습니다. 코드 예제에서 일부 변수를 변경하기 위해 코드 블록에서 동기화를 수행했지만 변수는 원래 휘발성으로 선언되었습니다. 고유 인스턴스를 휘발성 변수로 선언하고 sychronized로 나타낸 싱글 톤 클래스의 예에서 보았습니다. 그 인스턴스를 초기화하는 블록 ... 내 질문은 왜 우리가 그것을 동기화하는 동안 우리는 둘 다 할 필요가 우리가 그것을 휘발성 선언하는 이유는 무엇입니까 ?? 그들 중 하나가 아닌가?휘발성 동기 블록을 사용하는 이유는 무엇입니까?

public class someClass { 
volatile static uniqueInstance = null; 

public static someClass getInstance() { 
     if(uniqueInstance == null) { 
      synchronized(someClass.class) { 
       if(uniqueInstance == null) { 
        uniqueInstance = new someClass(); 
       } 
      } 
     } 
     return uniqueInstance; 
    } 

미리 감사드립니다.

+2

"volatile static uniqueInstance = null;"은 무엇입니까? ? –

답변

17

첫 번째 검사가 동기화 된 블록 내에있는 경우이 경우 자체적으로 동기화가 충분합니다 (하지만 변수가 휘발성이 아닌 경우 하나의 스레드가 다른 스레드에 의해 수행 된 변경 사항을 볼 수 없음). 원자 적으로 둘 이상의 작업을 수행해야하기 때문에 휘발성만으로는 충분하지 않습니다. 그러나 조심하십시오! 여기에있는 것은 소위 double-checked locking - 일반적인 관용구입니다. 불행하게도 does not work reliably입니다. Java 1.6 이후로 변경되었다고 생각하지만, 여전히 이런 종류의 코드는 위험 할 수 있습니다.

EDIT : 변수가 휘발성 인 경우이 코드는 JDK 5 (이전에 쓴 6 개가 아닌) 이후 올바르게 작동하지만 JDK 1.4 이하에서는 예상대로 작동하지 않습니다.

+2

당신은 첫 번째 검사가 동기화 된 블록 내에 있다면 동기화가 충분하다고 말합니다.하지만 동기 블록을 입력 한 후 동일한 검사를 다시 수행하므로 다음 스레드는 변수의 업데이트 된 값을 볼 수 있습니다. – Dorgham

+0

각 스레드는 다른 스냅 샷을 볼 수 있습니다. 따라서 한 스레드가 동기화 된 블록 내에서 변수를 읽는다 고해서 동기화하지 않고 변수를 읽는 다른 스레드가 동일한 상태를 볼 수는 없습니다. 스레드가 모두 적절한 동기화를 사용하는 경우에만 상태가 스레드간에 일관성을 유지합니다. 앞서 언급했듯이 Java 5 이상의 경우에는 '휘발성'이 처리됩니다 (즉, '휘발성'및 '동기화 됨'은 각각 메모리 장벽을 유발할뿐만 아니라 동일한 장벽을 사용함). –

+2

미래의 독자들과 똑같이 : 위 링크 된 기사는 시대에 뒤쳐져 있으며, 편집 상태에 따라이 기술은 거의 10 년 전에 출시 된 JDK 5에서 잘 작동합니다. –

4

This post은 휘발성의 배경을 설명합니다.

또한 정액 작업 인 Java Concurrency in Practice에서 다루어집니다.

주요 아이디어는 동시성뿐만 아니라 공유 상태뿐만 아니라 스레드 간의 상태의 가시성의 보호를 포함한다는 것입니다 : 휘발성이 들어오는 곳이다 (이 큰 계약이 Java Memory Model에 의해 정의된다.)

6

이. 이중 확인 잠금을 사용하는 경우 if(uniqueInstance == null)이 동기화 된 부분 내에 있지 않음에 유의하십시오. uniqueInstance 휘발성 없으면

, 그것은 그것의 일부가 블록 synchronized 실행하는 스레드가 아닌 다른 표시되지 않는 부분적으로 구성된 객체 "초기화"될 수있다. 휘발성 (volatile)은이 경우 모든 작업 또는 아무 작업도 수행하지 않습니다.

동기화 된 블록이 없으면이 시점까지 2 개의 스레드가 동시에 종료 될 수 있습니다.

if(uniqueInstance == null) { 
     uniqueInstance = new someClass(); <---- here 

그리고 두 개의 SomeClass 객체를 구성하여 목적을 이길 수 있습니다.

엄밀히는 휘발성이 필요하지 않습니다, 말하기, 방법은
public static someClass getInstance() { 
    synchronized(FullDictionary.class) { 
     if(uniqueInstance == null) { 
      uniqueInstance = new someClass(); 
      } 
     return uniqueInstance; 
    } 
} 

을 수 있었다하지만 그건의 getInstance를 수행하는 모든 스레드의 동기화 및 직렬화를 초래한다().

+0

동기화 비용을 줄이기 위해 if 문 다음에 동기화를 했으므로 동기화 블록을 입력 한 후 다시 확인하는 동안 제 질문이 왜 휘발성으로 만들어야합니까? 동기는 모든 공유 변수를 즉시 업데이트 할 것이므로 다음 스레드는 uniqueInstance의 업데이트 된 값을 볼 것입니다. – Dorgham

+1

@ user1262445 내가 말했듯이 휘발성 스레드는 객체가 부분적으로 생성 된 것을 볼 수 있습니다. 따라서 synchronized 블록으로 들어 가지 않고 uniqueInstance가 휘발성이 아닌 경우 부분적으로 생성 된 객체를 반환합니다. 나는. 당신은 1 개의 스레드가 synch 블록에 들어 가지 않고 다른 스레드가 synchornized 블록의 중간에있는 것처럼 쓰레기를 반환 할 수 있습니다. – nos

+0

내 질문에 'volititle'이 없다. 실제로 if (uniqueInstance == null) {는 초기화되었다고해도 패스 될 수 있지만 동기화 된 블록에 들어간 후 미스 판단은 동기화되었으므로 고정 될 것이다 그래서'if (uniqueInstance == null)'이 false가되고 새로운 인스턴스가 생성되지 않을 것인가? 따라서 valitle이 없어도 코드는 여전히 예상대로 작동합니다 (동일한 인스턴스가 생성되어 반환됩니다). 그러나 실제로 실적이 좋지 않습니까? 내가 맞습니까? – Jaskey

0

동기화 된 블록을 사용하지 않고도 동기화를 수행 할 수 있습니다. 휘발성 변수를 사용할 필요가 없습니다 ... 휘발성은 메인 메모리에서 하나의 변수를 업데이트합니다 ..and 는 메인 메모리에서 액세스 한 모든 공유 변수를 업데이트합니다. 그래서 당신은 요구 사항 ..

-1

내 두 센트 여기

프리스트 코드

if(uniqueInstance == null) { 
     synchronized(someClass.class) { 
      if(uniqueInstance == null) { 
       uniqueInstance = new someClass(); 
      } 
     } 
    } 

가 두번 uniqueInstance == 널을 확인되는 동기 블록 호출의 오버 헤드를 감소시키는 이유 직관의 짧은 설명 상대적으로 느리다. 이중 잠금이라고도합니다.

둘째, synchronized를 사용하는 이유는 이해하기 쉽기 때문에 동기화 된 블록 내부의 두 작업을 원자 적으로 만듭니다.

마지막 휘발성 수정자는 모든 스레드가 동일한 복사본을 볼 수 있도록하므로 동기화 된 블록 외부의 첫 번째 검사는 동기화 된 블록과 의 "동기화 된"방식으로 uniqueInstance의 값을 보게됩니다. 휘발성 변경자가 없으면 한 스레드가 uniqueInstance에 값을 할당 할 수 있지만 다른 스레드는 첫 번째 검사에서 값을 볼 수 없습니다. (두 번째 검사에서 확인할 수 있지만)