이중 체크 잠금에서 휘발성이 사용되는 이유
에서 헤드 퍼스트 디자인 패턴 책을 두 번 확인 잠금과 싱글 톤 패턴은 아래와 같이 구현되었습니다 :
public class Singleton {
private volatile static Singleton instance;
private Singleton() {}
public static Singleton getInstance() {
if (instance == null) {
synchronized (Singleton.class) {
if (instance == null) {
instance = new Singleton();
}
}
}
return instance;
}
}
왜 volatile
사용되고 있는지 이해가 안 돼요 . volatile
사용이 이중 확인 잠금, 즉 성능을 사용하는 목적을 무효화 하지 않습니까?
volatile
필요한 이유를 이해하기위한 좋은 자료 는 JCIP 책 에서 나옵니다 . Wikipedia에는 그 자료에 대한 적절한 설명 도 있습니다.
실제 문제는 구성이 완료되기 전에 Thread A
메모리 공간을 할당 할 수 있다는 instance
것입니다 instance
. Thread B
그 과제를보고 그것을 사용하려고 할 것입니다. 이로 Thread B
인해 부분적으로 구성된 버전의 instance
.
@irreputable이 인용했듯이 휘발성은 비싸지 않습니다. 비용이 많이 들더라도 성능보다 일관성이 우선되어야합니다.
Lazy Singleton을위한 또 하나의 깔끔하고 우아한 방법이 있습니다.
public final class Singleton {
private Singleton() {}
public static Singleton getInstance() {
return LazyHolder.INSTANCE;
}
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
}
소스 기사 : Wikipedia의 Initialization-on-demand_holder_idiom
소프트웨어 엔지니어링에서 Initialization on Demand Holder (디자인 패턴) 관용구는 지연로드 된 싱글 톤입니다. 모든 버전의 Java에서이 관용구는 우수한 성능으로 안전하고 동시적인 지연 초기화를 가능하게합니다.
클래스에는 static
초기화 할 변수 가 없기 때문에 초기화는 간단하게 완료됩니다.
그 안의 정적 클래스 정의 LazyHolder
는 JVM이 LazyHolder를 실행해야한다고 결정할 때까지 초기화되지 않습니다.
정적 클래스 LazyHolder
는 정적 메서드 getInstance
가 Singleton 클래스에서 호출 될 때만 실행 되며 처음 발생하면 JVM이 LazyHolder
클래스를 로드하고 초기화합니다 .
이 솔루션은 특수한 언어 구조 (예 : volatile
또는 synchronized
) 없이 스레드로부터 안전 합니다.
음, 성능을 위해 이중 확인 된 잠금이 없습니다. 그것은이다 깨진 패턴.
옆 감정을 떠나, volatile
그것을하지 않고 두 번째 스레드가 통과하는 시간 때문에 여기에 instance == null
, 첫 번째 스레드가 구성되지 않을 수도 있습니다 new Singleton()
개체의 창조한다는 하나의 약속 : 아직 전에-발생 에 할당 instance
임의의 thread하지만 하나 실제로 객체를 생성합니다.
volatile
차례로 읽기와 쓰기 사이에 발생 전 관계를 설정 하고 끊어진 패턴을 수정합니다.
성능을 찾고 있다면 홀더 내부 정적 클래스를 대신 사용하십시오.
만약 당신이 그것을 가지고 있지 않다면, 두 번째 쓰레드는 첫 번째 그것을 null로 설정 한 후에 동기화 된 블록에 들어갈 수 있고, 당신의 로컬 캐시는 여전히 그것이 null이라고 생각할 것입니다.
첫 번째는 정확성을위한 것이 아니라 (당신이 맞다면 스스로 패배하는 것이 맞다면) 최적화를위한 것입니다.
휘발성 읽기는 그 자체로 비싸지 않습니다.
getInstance()
휘발성 읽기의 영향을 관찰하기 위해 엄격한 루프에서 호출하는 테스트를 설계 할 수 있습니다 . 그러나 그 테스트는 현실적이지 않습니다. 이러한 상황에서 프로그래머는 일반적으로 getInstance()
한 번 호출 하고 사용 기간 동안 인스턴스를 캐시합니다.
Another impl is by using a final
field (see wikipedia). This requires an additional read, which may become more expensive than the volatile
version. The final
version may be faster in a tight loop, however that test is moot as previously argued.
Declaring the variable as volatile
guarantees that all accesses to it actually read its current value from memory.
Without volatile
, the compiler may optimize away the memory accesses and keep its value in a register, so only the first use of the variable reads the actual memory location holding the variable. This is a problem if the variable is modified by another thread between the first and second access; the first thread has only a copy of the first (pre-modified) value, so the second if
statement tests a stale copy of the variable's value.
참고URL : https://stackoverflow.com/questions/7855700/why-is-volatile-used-in-double-checked-locking
'Programing' 카테고리의 다른 글
개체가 아닌 멤버 함수 호출 (0) | 2020.11.12 |
---|---|
URL의 글자 수 제한은 얼마입니까? (0) | 2020.11.12 |
문자열 이름으로 클래스 속성 설정 / 가져 오기 (0) | 2020.11.12 |
ConfigurationManager.AppSettings [Key]는 매번 web.config 파일에서 읽습니까? (0) | 2020.11.12 |
JavaScript : 빈 배열, []는 조건부 구조에서 참으로 평가됩니다. (0) | 2020.11.12 |