1. 程式人生 > >並發之AtomicIntegerArray

並發之AtomicIntegerArray

IT atom ini ont ring uid 理解 class ger

5 並發之AtomicIntegerArray 該類是Java對Integer數組支持的原子性操作;在認識這個類之前我們先來看一個方法,這個方法是Integer類中的; public static int numberOfLeadingZeros(int i) {} 看看官方的解釋吧:返回具有至多單個 1 位的 int 值,在指定的 int 值中最高位(最左邊)的 1 位的位置。 看完官方的解釋大家知道了吧;哈哈,說實話其實我也沒能理解;但是這並不妨礙我們對該方法的使用; 該方法直譯過來就是抵達0位置的個數: 先來看一段代碼吧:
public class IntegerArrayTest {
     
public static void main(String[] args) { Integer a=1; Integer b=13; Integer c=25; /** * 0000 0000 0000 0000 0000 0000 0000 0000 * 0000 0000 0000 0000 0000 0000 0000 0001 0的個數為31 1 * 0000 0000 0000 0000 0000 0000 0000 1101 0的個數為28 13 * 0000 0000 0000 0000 0000 0000 0001 1001 0的個數為27 25 * a的二進制為=1 最左邊開始數起連續0的個數為31 * b的二進制為=1101 最左邊開始數起連續0的個數為28 * c的二進制為=11001 最左邊開始數起連續0的個數為27
*/ System.out.println("a的二進制為="+Integer.toBinaryString(a)+" 最左邊開始數起連續0的個數為"+Integer.numberOfLeadingZeros(a)); System.out.println("b的二進制為="+Integer.toBinaryString(b)+" 最左邊開始數起連續0的個數為"+Integer.numberOfLeadingZeros(b)); System.out.println("c的二進制為="+Integer.toBinaryString(c)+" 最左邊開始數起連續0的個數為"+Integer.numberOfLeadingZeros(c)); } }
public class AtomicIntegerArray implements java.io.Serializable { private static final long serialVersionUID = 2862133569453604235L; private static final Unsafe unsafe = Unsafe.getUnsafe(); private static final int base = unsafe.arrayBaseOffset(int[].class); private static final int shift; private final int[] array; static { int scale = unsafe.arrayIndexScale(int[].class); if ((scale & (scale - 1)) != 0) throw new Error("data type scale not a power of two"); shift = 31 - Integer.numberOfLeadingZeros(scale); } }

在看上述的代碼之前,我先來說一個問題: 在AutomicInteger類中,value的值是使用了volatile修飾的;會對所有的線程立即可見;但是本身又不可以保證原子性的操作;那麽為什麽還要在該類中使用這個關鍵字呢?
在這裏我需要做一個補充說明;看如下的代碼所示(是AutomicIntegerArray中的方法):
1   /**
2      * 原子性的設置一個值
3      */
4     public final void set(int i, int newValue) {
5         unsafe.putIntVolatile(array, checkedByteOffset(i), newValue);
6     }

下面的源碼是AutomicInteger類中的set()方法
 /**
     * 原子性的設置一個值
     */
    public final void set(int newValue) {
        value = newValue;
    }

大家看到了吧,兩個方法的本質是一樣的,第一個方法使用了CAS算法,並且獲原子性的設置了這個數組中元素的值;之所以使用volatile是對其它線程立馬可見;這個才是最重要的原因;而對於lazySet()方法而言,是沒有使用volatile關鍵字的;因為它對其他線程是屬於賴設置的;也就是不能馬上被其他線程可見;也就是說可以變量共享,但是使用了volatile將失去JVM的優化作用;原子性操作我認為大多數操作或者讀的操作沒有使用volatile的關鍵是讀可以多線程共享,寫不是共享的;其實CAS算法設計的就是樂觀鎖的思想;
public static void automicIntegerArray() {
         /**
          * 創建一個原子性的數組,長度為5
          */
         AtomicIntegerArray atomicArray = new AtomicIntegerArray(5);
         /**
          * 設置第一個元素的值為5
          */
         atomicArray.set(0, 5);
         /**
          * 第一個元素減去1
          */
         int current = atomicArray.decrementAndGet(0);
         System.out.println("current = " + current);//current = 4
     }

看AutomicIntegerArray的構造器
/**
     * 創建一個原子性整※數組,並且長度為length;而且初始值都為0
     * elements initially zero.
     */
    public AtomicIntegerArray(int length) {
        array = new int[length];
    }

並發之AtomicIntegerArray