首頁 > 軟體

Java並行程式設計之原子操作類詳情

2022-04-15 22:00:05

JUC包提供了一系列的原子性操作類,這些類都是使用非阻塞演演算法CAS實現的,相比使用鎖實現原子性操作者在效能上有很大提升。JUC包中含有AtomicInteger、AtomicLong、AtomicBoolean,它們的原理類似。下面我們以AtomicLong為例來講解。

我們先來看一下部分原始碼:

public class AtomicLong extends Number implements java.io.Serializable {
    private static final long serialVersionUID = 1927816293512124184L;

    //1.獲取Unsafe類範例
    private static final Unsafe unsafe = Unsafe.getUnsafe();
    //2.存放value的偏移量
    private static final long valueOffset;
    static final boolean VM_SUPPORTS_LONG_CAS = VMSupportsCS8();

    //3.用於判斷是否支援Long型別無鎖CAS
    private static native boolean VMSupportsCS8();

    static {
        try {
            //4.獲取value在AtomicLong中的偏移量
            valueOffset = unsafe.objectFieldOffset
                (AtomicLong.class.getDeclaredField("value"));
        } catch (Exception ex) { throw new Error(ex); }
    }
    //5.實際變數值
    private volatile long value;

    /**
     * Creates a new AtomicLong with the given initial value.
     *
     * @param initialValue the initial value
     */
    public AtomicLong(long initialValue) {
        value = initialValue;
    }
    ············省略部分程式碼·············
}

上面程式碼中,程式碼1處通過Unsafe.getUnsafe()獲取到Unsafe類的範例(因為AtomicLong類是在rt.jar包下面的,AtomicLong類就是通過Bootstarp類載入器進行載入的)。程式碼5處,value被宣告為volatile型別,保證記憶體的可見性。通過程式碼2,4獲取value變數在AtomicLong類中的偏移量。

接下來介紹一下AtomicLong中的主要函數:

  • 遞增和遞減程式碼
//呼叫unsafe方法,設定value=value+1後,返回原始的值
public final long getAndIncrement() {
    return unsafe.getAndAddLong(this, valueOffset, 1L);
}

//呼叫unsafe方法,設定value=value-1後,返回原始的值
public final long getAndDecrement() {
    return unsafe.getAndAddLong(this, valueOffset, -1L);
}


//呼叫unsafe方法,設定value=value+1後,返回遞增後的值
public final long incrementAndGet() {
    return unsafe.getAndAddLong(this, valueOffset, 1L) + 1L;
}

//呼叫unsafe方法,設定value=value-1後,返回遞減後的值
public final long decrementAndGet() {
    return unsafe.getAndAddLong(this, valueOffset, -1L) - 1L;
}

上面的四個函數內部都是通過呼叫Unsafe的getAndAddLong方法來實現操作,這個函數是個原子性操作,這裡第一個引數是AtomicLong範例的參照的,第二個引數是value變數在AtomicLong的偏移值,第三個引數是要設定的第二個變數的值。

其中getAndIncrement()方法在JDK7中實現邏輯為:

public final long getAndIncrement() {
    while(true) {
        long current = get();
        long next = current + 1;
        if (compareAndSet(current, next))
            return current;
    }
}

如上程式碼中,每個執行緒是先拿到變數的當前值(由於value是volatile變數,所以這是獲取的最新值),然後在工作記憶體中對其進行增1操作,而後使用CAS修改變數的值,如果設定失敗,則迴圈繼續嘗試,直到設定成功。

JDK8中的邏輯為:

public final long getAndIncrement() {
        retrturn unsafe.getAndAddLong(this, valueOffset, 1L);
    }

其中JDK8中的unsafe.getAndAddLong的程式碼為:

public final long getAndAddLong(Object var1, long var2, long var4) {
   long var6;
   do {
       var6 = this.getLongVolatile(var1, var2);
   } while(!this.compareAndSwapLong(var1, var2, var6, var6 + var4));

   return var6;
}

從中可以看到,JDK7中的AtomicLong中迴圈邏輯已經被JDK8中的原子操作類Unsafe內建了。

  • boolean compareAndSet(long expect,long update)
public final boolean compareAndSet(long expect,long update) 
{
    return unsafe.compareAndSwapLong(this, valueOffset, expect, update);
}

函數在內部呼叫了unsafe.compareAndSwapLong方法。如果原子變數中的value值等於expect,則使用update值更新該值並返回true,否則返回false。

到此這篇關於Java並行程式設計之原子操作類詳情的文章就介紹到這了,更多相關Java 原子操作類內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!


IT145.com E-mail:sddin#qq.com