ConcurrentHashMap에 대한 JDK의 소스 코드를 읽었습니다.
하지만 다음 코드가 저를 혼란스럽게 했습니다:
public boolean isEmpty() {
final Segment<K,V>[] segments = this.segments;
...
}
제 질문은:
<이.세그먼트>가 선언되었습니다:
final Segment<K,V>[] segments;
따라서 여기 메서드의 시작 부분에서 동일한 유형 참조를 선언하고 동일한 메모리를 가리킵니다.
작성자는 왜 이런 식으로 작성했을까요? 이 세그먼트를 직접 사용하지 않은 이유는 무엇일까요? 무슨 이유가 있을까요?
이것은 '휘발성' 변수를 포함하는 잠금 없는 코드에 일반적으로 사용되는 관용구입니다. 첫 번째 줄에서 휘발성
을 한 번 읽은 다음 작업합니다. 그 동안 다른 스레드가 휘발성
을 업데이트할 수 있지만 처음에 읽은 값에만 관심이 있습니다.
또한 문제의 멤버 변수가 휘발성이 아니라 최종 변수인 경우에도 스택 위치에서 읽는 것이 임의의 힙 위치에서 읽는 것보다 캐시 친화적이므로 이 관용구는 CPU 캐시와 관련이 있습니다. 또한 로컬 변수가 CPU 레지스터에 바인딩될 가능성이 더 높습니다.
후자의 경우 일반적으로 JIT 컴파일러가 이러한 문제를 처리하기 때문에 실제로 약간의 논란이 있지만, Doug Lea는 일반적인 원칙을 고수하는 사람 중 한 명입니다.
성능을 고려해서 필드 값을 한 번만 검색하면 되기 때문인 것 같습니다.
효과적인 자바의 싱글톤 이디엄을 참고할 수 있습니다.
그의 싱글톤은 여기에 있습니다;
private volatile FieldType field;
FieldType getField() {
FieldType result = field;
if (result == null) {
synchronized(this) {
result = field;
if (result == null)
field = result = computeFieldValue();
}
}
return result;
}
그리고 그는 이렇게 썼습니다:
이 코드는 약간 복잡해 보일 수 있습니다. 특히 지역 변수 결과가 불분명할 수 있습니다. 이 변수가 하는 일은 다음과 같습니다. 일반적인 경우 필드가 한 번만 읽히도록 하는 것입니다. 이미 초기화된 경우입니다. 반드시 필요한 것은 아니지만, 이 변수를 사용하면 성능을 향상시킬 수 있으며 저수준에 적용되는 표준에 따라 더 우아합니다. 동시 프로그래밍. 제 컴퓨터에서 위의 방법은 약 25 지역 변수가 없는 명백한 버전보다 약 25퍼센트 더 빠릅니다.