원자류 소스 분석

11442 단어

AtomicInteger


Java는 1.5부터 막히지 않는 라인의 안전한 포장 종류, 예를 들어 AtomicInteger, AtomicLong 등을 제공하는데 이런 실현은 대동소이하다. 여기에는 AtomicInteger를 예로 들 수 있다.
AtomicInteger의 작업은 모두 Unsafe 클래스를 바탕으로 하는데 이 클래스는 JDK 내부에서 사용하는 도구 클래스로 하드웨어 수준의 원자 조작을 제공하고 관련 소개 스탬프를 제공한다.https://www.jianshu.com/p/2e5b92d0962e.다음 코드는 JDK1.8.먼저 멤버 변수를 살펴보겠습니다.
 1 private static final Unsafe unsafe = Unsafe.getUnsafe();
 2 // value      
 3 private static final long valueOffset;
 4 
 5 static {
 6     try {
 7         //   value      
 8         valueOffset = unsafe.objectFieldOffset
 9             (AtomicInteger.class.getDeclaredField("value"));
10     } catch (Exception ex) { throw new Error(ex); }
11 }
12 //  
13 private volatile int value;

value는 구체적인 값이다. 이 값은volatile 변수로 메모리의 가시성을 확보하고 다른valueOffset 변수는 중요도가value와 비슷한 변수이다. 이 두 변수의 작용을 이해하기만 하면 AtomicInteger는 기본적으로 이 변수가 왜 필요한지 알 수 있다. 구체적인 이유는 관련 방법을 보십시오.
AtomicInteger 클래스의 방법은 모두 Unsafe에 기반을 두고 있습니다. 관련 방법의 역할은 처음에 제시한 링크나 자체 구글을 보십시오. 여기서value와value Offset의 역할만 설명하는 방법만 소개합니다.
1 public final int get() {
2     return value;
3 }
4 
5 public final void set(int newValue) {
6     value = newValue;
7 }

get과 set 방법을 제외하고 모든 방법은valueOffset을 사용합니다. 그 이유는 volatile가 JVM의 메모리 재배열 등 최적화를 금지하기 때문입니다.그래서 다음은 다른 방법을 살펴보자.
1 public final int getAndSet(int newValue) {
2     return unsafe.getAndSetInt(this, valueOffset, newValue);
3 }

이 방법은 현재 값을 획득한 다음 값을 newValue로 설정하여 Unsafe 방법을 살펴보는 것입니다.
1 public final int getAndSetInt(Object var1, long var2, int var4) {
2     int var5;
3     do {
4         var5 = this.getIntVolatile(var1, var2);//        var1        var2     
5     } while(!this.compareAndSwapInt(var1, var2, var5, var4));//   CAS  ,       var4   true
6 
7     return var5;//     
8 }

이를 통해 알 수 있듯이 이 방법은 낙관적 자물쇠 메커니즘을 채택했고 사실상 Unsafe는 모두 낙관적 자물쇠를 채택했다.set 방법과lazyset 방법의 차이는 원자포장류가 가장 많이 언급한 문제이다. set은volatile 변수의 값을 부여하여 가시성을 확보하지만 일정한 성능을 손실시킨다(volatile는store-load 장벽을 추가하고 재배열 금지하기 때문에). lazyset은Unsafe류의putOrderedInt 방법을 사용한다. JDK 정부는 이것에 대해putOrderedInt 방법 이후에 지령 재배열이 존재하기 때문에 가시성을 보장할 수 없다고 설명했다.그러나putOrderedInt에서 사용하는 메모리 장벽(store-store)은volatile에서 사용하는 메모리 장벽(store-load)보다 성능이 좋기 때문에lazyset 방식은 성능을 보장하지만 가시성을 손상시킬 수 있다.총괄적으로 말하면 AtomicInteger는 Unsafe 클래스를 바탕으로 이루어진 것이다. 그 중에서 value 저장소의 구체적인 값은 가시성을 확보하기 위해 사용하는 것이고 value Offset은 value의 메모리 편이량을 저장하는데 Unsafe 클래스를 사용할 때 사용하는 이 변수로 성능을 확보하는 데 주요한 역할을 한다.

AtomicIntegerArray


AtomicIntegerArray의 방법은 Unsafe 클래스를 바탕으로 하는 것이기 때문에 AtomicInteger의 방법과 유사하다. 여기에 소개하지 않고 구성원 변수만 분석한다.Unsafe 클래스의 작업은 메모리 오프셋을 기반으로 하는 것으로 알고 있으므로 AtomicIntegerArray에서 배열 요소의 메모리 오프셋을 계산하는 방법에 초점을 맞춥니다.
 1 private static final Unsafe unsafe = Unsafe.getUnsafe();
 2 //       
 3 private static final int base = unsafe.arrayBaseOffset(int[].class);
 4 //            
 5 private static final int shift;
 6 //     
 7 private final int[] array;
 8 
 9 static {
10     //                ,   int   4   
11     int scale = unsafe.arrayIndexScale(int[].class);
12     if ((scale & (scale - 1)) != 0)
13         throw new Error("data type scale not a power of two");
14     // 2.       
15     shift = 31 - Integer.numberOfLeadingZeros(scale);
16 }
17 
18 private long checkedByteOffset(int i) {
19     if (i < 0 || i >= array.length)
20         throw new IndexOutOfBoundsException("index " + i);
21 
22     return byteOffset(i);
23 }
24 
25 // 1.          
26 private static long byteOffset(int i) {
27     return ((long) i << shift) + base;
28 }

코드를 설명하기 전에, 먼저 그룹 주소의 분배를 알아보세요.int수조를 예로 들면 int는 4바이트를 차지하고 수조의 첫 번째 주소가 0이라고 가정하면 i=0의 주소는 0이고 i=1의 주소는 4이며 i=2의 주소는 8이다.아래에 표시된 i와 주소의 관계는addr=4*i=i<2이고, 그룹의 첫 번째 주소가base라면 addr=(i<2)+base입니다.우리는 코드에 표시된 순서에 따라 설명한다.비트 오프셋 방법은 그룹 요소의 실제 편이량을 얻는 것입니다. 그룹 아래에 i를 표시하고 왼쪽으로shift 위치를 이동한 다음에 그룹의 첫 번째 주소를 더하면 그룹의 첫 번째 주소입니다. 이shift는 단위 편이량이라고 부릅니다.  2. numberOfleadingZeros 이 방법은 매개 변수의 고위 연속 0의 개수를 얻는 것입니다.scale의 값은 4이고 2진법으로 쓰면 0000 0000 0000 0000 0000 0000 0000 0100이다. 마지막으로number Of Leading Zeros의 반환값은 29이다. 1 앞에 0이 29개 있기 때문에 마지막으로shift=2를 계산한다.위에서 설명한 바와 같이 Atomic Integer Array가 수조 중의 요소의 주소를 어떻게 계산하는지 이미 아실 겁니다.여기서 요약하자면 앞의addr=4*i+base를 변형시키고 수조 요소가 2를 차지하는 시프트 차멱 바이트를 가정하면addr=[(2^shift)*i]+base=(i<
 
전재 대상:https://www.cnblogs.com/ouhaitao/p/11242721.html

좋은 웹페이지 즐겨찾기