답변:
"JDK-6275329 : 원자 클래스에 lazySet 메서드 추가" 에서 직접 인용 :
Mustang의 마지막 JSR166 후속 작업으로 Atomic 클래스 (AtomicInteger, AtomicReference 등)에 "lazySet"메서드를 추가했습니다. 이는 비 차단 데이터 구조를 사용하여 코드를 미세 조정할 때 유용한 틈새 방법입니다. 의미는 쓰기가 이전 쓰기로 재정렬되지 않도록 보장되지만 다른 휘발성 쓰기 또는 동기화 작업이 발생할 때까지 후속 작업으로 재정렬 될 수 있습니다 (또는 동등하게 다른 스레드에 표시되지 않을 수 있음).
주된 사용 사례는 오로지 장기적인 가비지 보존을 피하기 위해 비 차단 데이터 구조에서 노드의 필드를 무효화하는 것입니다. 다른 스레드가 잠시 동안 null이 아닌 값을 보는 경우 무해 할 때 적용되지만 구조가 결국 GCable인지 확인하고 싶습니다. 이러한 경우 null 휘발성 쓰기 비용을 피하여 더 나은 성능을 얻을 수 있습니다. 참조 기반이 아닌 원자에 대한 이러한 라인을 따라 몇 가지 다른 사용 사례가 있으므로 메서드는 모든 AtomicX 클래스에서 지원됩니다.
일반적인 멀티 프로세서의 기계 수준 장벽으로 이러한 작업을 생각하려는 사람들을 위해 lazySet은 선행 상점-점포 장벽 (현재 플랫폼에서 작동하지 않거나 매우 저렴함)을 제공하지만 상점 부하 장벽은 없습니다. (일반적으로 휘발성 쓰기의 비용이 많이 드는 부분입니다).
Atomic*
in 범위 를 가진 모든 스레드에 상태 변경이 표시되지 않을 수 있음 ).
lazySet은 rmw 스레드 간 통신에 사용할 수 있습니다. xchg는 원자 적입니다. 가시성에 관해서는 라이터 스레드 프로세스가 캐시 라인 위치를 수정할 때 인텔 CPU의 캐시 일관성 프로토콜이 보장하기 때문에 리더 스레드의 프로세서가 다음 읽기에서이를 볼 것입니다. LazySet은 작동하지만 캐시 라인은 다음에 읽을 때 업데이트됩니다. CPU는 충분히 현대적이어야합니다.
http://sc.tamu.edu/systems/eos/nehalem.pdf 다중 프로세서 플랫폼 인 Nehalem의 경우 프로세서는 시스템 메모리에 대한 다른 프로세서의 액세스를 위해 주소 버스를 "스누핑"(도청) 할 수 있습니다. 내부 캐시에. 이 스누핑 기능을 사용하여 내부 캐시를 시스템 메모리 및 다른 상호 연결된 프로세서의 캐시와 일관되게 유지합니다. 스누핑을 통해 한 프로세서가 현재 공유 상태에서 캐시 된 메모리 위치에 다른 프로세서가 쓰려고한다는 것을 감지하면 스누핑 프로세서는 캐시 블록을 무효화하여 다음에 동일한 메모리 위치에 액세스 할 때 캐시 라인 채우기를 수행하도록합니다. .
x86 cpu 아키텍처 용 oracle hotspot jdk->
lazySet == unsafe.putOrderedLong == xchg rw (nehelem intel cpu에서 20 사이클 비용이 드는 소프트 장벽 역할을하는 asm 명령어)
x86 (x86_64)에서 이러한 장벽은 volatile 또는 AtomicLong getAndAdd보다 성능면에서 훨씬 저렴합니다.
하나의 생산자, 하나의 소비자 대기열 시나리오에서 xchg 소프트 배리어는 당연히 새 데이터를 소비 (작업) 할 소비자 스레드 코드보다 생산자 스레드에 대해 lazySet (sequence + 1) 이전에 코드 줄을 강제 할 수 있습니다. 소비자 스레드는 compareAndSet (시퀀스, 시퀀스 + 1)을 사용하여 생산자 시퀀스가 정확히 1만큼 증가했는지 원자 적으로 확인해야합니다.
나는 핫스팟 소스 코드를 추적하여 lazySet과 cpp 코드의 정확한 매핑을 찾았습니다 : http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/9b0ca45cd756/src/share/vm/prims/unsafe. cpp Unsafe_setOrderedLong-> SET_FIELD_VOLATILE 정의-> OrderAccess : release_store_fence. x86_64의 경우 OrderAccess : release_store_fence는 xchg 명령어를 사용하여 정의됩니다.
jdk7에서 정확히 정의 된 방법을 볼 수 있습니다 (doug lea는 JDK 8의 새로운 기능에 대해 작업 중입니다) : http://hg.openjdk.java.net/jdk7/jdk7/hotspot/file/4fc084dac61e/src/os_cpu/ linux_x86 / vm / orderAccess_linux_x86.inline.hpp
또한 hdis를 사용하여 작동중인 lazySet 코드의 어셈블리를 디스 어셈블 할 수 있습니다.
또 다른 관련 질문이 있습니다. xchg를 사용할 때 mfence가 필요합니까?
lazySet 및 기본 putOrdered의 기원과 유용성에 대한 자세한 내용은 여기에서 찾을 수 있습니다. http://psy-lob-saw.blogspot.co.uk/2012/12/atomiclazyset-is-performance-win-for.html
요약하자면, lazySet은 저장소로드 펜스가 아닌 저장소 저장소 역할을한다는 점에서 약한 휘발성 쓰기입니다. 이것은 lazySet이 휘발성 집합에 사용되는 훨씬 더 비싼 명령이 아니라 컴파일러에 의해 다시 정렬 될 수없는 MOV 명령으로 컴파일되는 JIT로 귀결됩니다.
값을 읽을 때 항상 휘발성 읽기를 수행합니다 (어쨌든 Atomic * .get () 사용).
lazySet은 단일 작성자에게 일관된 휘발성 쓰기 메커니즘을 제공합니다. 즉, 단일 작성자가 lazySet을 사용하여 카운터를 증가시키는 것은 완벽하게 합법적입니다. 동일한 카운터를 증가시키는 여러 스레드는 CAS를 사용하여 경쟁하는 쓰기를 해결해야합니다. incAndGet에 대한 Atomic *의 커버.
StoreStore
장벽,하지만 하지StoreLoad
?
로부터 동시 원자 패키지의 개요
lazySet 은 일반적인 비 휘발성 쓰기로 재정렬 제약을 부과하지 않는 후속 (이전이 아닌) 메모리 작업으로 재정렬을 허용한다는 점을 제외하고는 휘발성 변수를 기록 (할당)하는 메모리 효과가 있습니다. 다른 사용 컨텍스트 중에서 lazySet은 가비지 수집을 위해 다시 액세스하지 않는 참조를 nulling 할 때 적용될 수 있습니다.
lazySet에 대해 궁금하다면 다른 설명도해야합니다.
원자의 액세스 및 업데이트에 대한 메모리 효과는 일반적으로 The Java ™ Language Specification의 섹션 17.4에 명시된대로 휘발성 규칙을 따릅니다.
get 은 휘발성 변수를 읽는 메모리 효과를가집니다.
set 은 휘발성 변수를 작성 (할당)하는 메모리 효과를가집니다.
lazySet 은 일반적인 비 휘발성 쓰기로 재정렬 제약을 부과하지 않는 후속 (이전이 아닌) 메모리 작업으로 재정렬을 허용한다는 점을 제외하고는 휘발성 변수를 기록 (할당)하는 메모리 효과가 있습니다. 다른 사용 컨텍스트 중에서 lazySet은 가비지 수집을 위해 다시 액세스하지 않는 참조를 nulling 할 때 적용될 수 있습니다.
weakCompareAndSet 은 변수를 원자 적으로 읽고 조건부로 작성하지만 순서 전 발생을 생성하지 않으므로 weakCompareAndSet의 대상이 아닌 변수의 이전 또는 후속 읽기 및 쓰기에 대한 보장을 제공하지 않습니다.
compareAndSet 및 getAndIncrement와 같은 다른 모든 읽기 및 업데이트 작업에는 휘발성 변수 읽기 및 쓰기의 메모리 효과가 있습니다.
여기에 내 이해가 있습니다. 내가 틀렸다면 정정 해주세요. lazySet()
"세미"휘발성으로 생각할 수 있습니다 . 기본적으로 다른 스레드에서 읽는 측면에서 비 휘발성 변수입니다. 즉, lazySet에 의해 설정된 값이 다른 사용자에게 표시되지 않을 수 있습니다. 스레드. 그러나 다른 쓰기 작업이 발생하면 휘발성이됩니다 (다른 스레드에서 발생할 수 있음). 내가 상상할 수있는 lazySet의 유일한 영향은 compareAndSet
. 따라서을 사용 lazySet()
하면 get()
다른 스레드에서 여전히 이전 값을 가져올 수 있지만 compareAndSet()
쓰기 작업이므로 항상 새 값을 갖게됩니다.
compareAndSet
않습니까?
Re : 그것을 벙어리 시도-
이는 특정 저장소 (예 : ref = null;) 작업에 대해 휘발성이 아닌 것처럼 휘발성 필드를 처리하는 방법으로 생각할 수 있습니다.
완벽하게 정확하지는 않지만 "OK, I really do n't care"와 "Hmm, 잠시 생각해 보겠습니다"사이에서 결정을 내릴 수 있으면 충분합니다.