귀하의 관용구는 참조 가 안전하게 게시 된 경우에만 안전 합니다 . 안전한 게시 는 자체 내부와 관련된 것이 아니라 구성 스레드가 다른 스레드에서 맵에 대한 참조를 표시하는 방법을 처리합니다.HashMap
HashMap
기본적으로 여기서 가능한 유일한 경주 HashMap
는 완전히 구성되기 전에 액세스 할 수있는 읽기 스레드와 읽기 스레드 사이의 구성입니다 . 대부분의 토론은지도 객체의 상태에 대한 문제에 관한 것이지만, 절대 수정하지 않기 때문에 이는 관련이 없습니다. 따라서 흥미로운 부분은 HashMap
참조가 게시되는 방법입니다.
예를 들어, 다음과 같이지도를 게시한다고 가정하십시오.
class SomeClass {
public static HashMap<Object, Object> MAP;
public synchronized static setMap(HashMap<Object, Object> m) {
MAP = m;
}
}
... 어느 시점 setMap()
에서 맵과 함께 호출되고 다른 스레드가 SomeClass.MAP
맵에 액세스하는 데 사용 하고 다음과 같이 null을 확인합니다.
HashMap<Object,Object> map = SomeClass.MAP;
if (map != null) {
.. use the map
} else {
.. some default behavior
}
이다 안전하지 그것이 것처럼 아마 나타나더라도. 문제가없는 것이 없다 일 - 전 세트 사이의 관계 SomeObject.MAP
와 다른 스레드에서 이후의 읽기, 읽기 스레드가 부분적으로 구축지도를 볼 무료 있도록. 이것은 거의 모든 작업을 수행 할 수 있으며 실제로 읽기 스레드를 무한 루프에 넣는 것과 같은 작업을 수행합니다 .
안전지도를 게시하려면이 설정해야합니다 발생-전에 사이의 관계 참조 서면으로 받는 HashMap
(즉, 출판 ) 및 해당 참조 (즉, 소비)의 후속 독자. 편리하게, 이를 달성 하기 위한 기억하기 쉬운 몇 가지 방법 이 있습니다 [1] :
- 올바르게 잠긴 필드를 통해 참조 교환 ( JLS 17.4.5 )
- 정적 초기화 프로그램을 사용하여 초기화 저장소를 수행하십시오 ( JLS 12.4 )
- 변동성 필드 ( JLS 17.4.5 )를 통해 또는이 규칙의 결과로 AtomicX 클래스를 통해 참조를 교환하십시오.
- 최종 필드 ( JLS 17.5 ) 로 값을 초기화하십시오 .
시나리오에서 가장 흥미로운 것은 (2), (3) 및 (4)입니다. 특히 (3)은 위의 코드에 직접 적용됩니다 MAP
.
public static volatile HashMap<Object, Object> MAP;
널 이 아닌 값 을 보는 독자는 반드시 상점과 관계가 있어야 하기MAP
때문에 맵 초기화와 연관된 모든 상점을 볼 수 있습니다.
(2) (정적 이니셜 라이저 사용) 및 (4) ( final 사용 ) 모두 MAP
런타임에 동적으로 설정할 수 없음을 의미하므로 다른 메소드는 메소드의 의미를 변경합니다 . 당신이하지 않으면 필요 그렇게, 그럼 그냥 선언 MAP
A와 static final HashMap<>
당신은 안전 게시를 보장합니다.
실제로 규칙은 "수정되지 않은 개체"에 안전하게 액세스하기 위해 간단합니다.
본질적으로 불변 이 아닌 객체를 게시하는 경우 (선언 된 모든 필드에서 final
와 같이)
- 선언 순간에 할당 될 객체를 이미 만들 수 있습니다. a :
final
필드를 사용하십시오 ( static final
정적 멤버 포함).
- 참조가 이미 표시된 후에 나중에 오브젝트를 지정하려고합니다 . 휘발성 필드 b를 사용하십시오 .
그게 다야!
실제로는 매우 효율적입니다. a의 사용 static final
분야는, 예를 들어, JVM은 값이 프로그램의 삶에 대한 변경 가정하고 무겁게을 최적화 할 수 있습니다. (A)의 사용 final
부재 필드 있도록 대부분의 구조는 일반 필드 판독하는 방법 당량의 필드를 판독하고 억제되지 않는 상기 최적화 된 C를 .
마지막으로 사용하면 volatile
영향을 미칩니다. 많은 아키텍처 (예 : x86, 특히 읽기가 읽기를 허용하지 않는 아키텍처)에는 하드웨어 장벽이 필요하지 않지만 컴파일시 일부 최적화 및 재정렬이 발생하지 않을 수 있습니다. 효과는 일반적으로 작습니다. 그 대가로 실제로 요청한 것보다 더 많은 것을 얻을 수 있습니다. 안전하게 게시 할 수있을뿐만 아니라 동일한 참조에 대해 원하는만큼 HashMap
수정하지 않은 수를 더 많이 저장할 수 HashMap
있으며 모든 독자에게 안전하게 게시 된지도가 표시됩니다. .
자세한 내용은 Shipilev 또는 Manson and Goetz의 FAQ를 참조하십시오 .
[1] shipilev 에서 직접 인용 .
a 복잡해 보이지만, 선언 시점이나 생성자 (멤버 필드) 또는 정적 초기화 기 (정적 필드)에서 생성시 참조를 할당 할 수 있습니다.
b 선택적으로 synchronized
메소드를 사용 하여 가져 오거나 설정할 수 AtomicReference
있지만 할 수있는 최소 작업에 대해 이야기하고 있습니다.
c 매우 약한 메모리 모델을 가진 일부 아키텍처 ( 알파, 나는 당신을보고 있습니다 )는 읽기 전에 어떤 유형의 읽기 장벽이 필요할 수 final
있지만 오늘날에는 매우 드 rare니다.