第十一章 AtomicInteger源碼解析


1、原子類

  • 可以實現一些原子操作
  • 基於CAS

下面就以AtomicInteger為例。

 

2、AtomicInteger

在沒有AtomicInteger之前,對於一個Integer的線程安全操作,是需要使用同步鎖來實現的,當然現在也可以通過ReentrantLock來實現,但是最好最方便的實現方式是采用AtomicInteger。

具體示例:

package com.collection.test;

import java.util.concurrent.atomic.AtomicInteger;

/**
 * 原子類的測試
 */
public class AtomicTest {
    private static AtomicInteger atomicInteger = new AtomicInteger();
    
    //獲取當前值
    public static void getCurrentValue(){
        System.out.println(atomicInteger.get());//-->0
    }
    
    //設置value值
    public static void setValue(){
        atomicInteger.set(12);//直接用12覆蓋舊值
        System.out.println(atomicInteger.get());//-->12
    }
    
    //根據方法名稱getAndSet就知道先get,則最后返回的就是舊值,如果get在后,就是返回新值
    public static void getAndSet(){
        System.out.println(atomicInteger.getAndSet(15));//-->12
    }
    
    public static void getAndIncrement(){
        System.out.println(atomicInteger.getAndIncrement());//-->15
    }
    
    public static void getAndDecrement(){
        System.out.println(atomicInteger.getAndDecrement());//-->16
    }
    
    public static void getAndAdd(){
        System.out.println(atomicInteger.getAndAdd(10));//-->15
    }
    
    public static void incrementAndGet(){
        System.out.println(atomicInteger.incrementAndGet());//-->26
    }
    
    public static void decrementAndGet(){
        System.out.println(atomicInteger.decrementAndGet());//-->25
    }
    
    public static void addAndGet(){
        System.out.println(atomicInteger.addAndGet(20));//-->45
    }
    
    public static void main(String[] args) {
        AtomicTest test = new AtomicTest();
        test.getCurrentValue();
        test.setValue();
        //返回舊值系列
        test.getAndSet();
        test.getAndIncrement();
        test.getAndDecrement();
        test.getAndAdd();
        //返回新值系列
        test.incrementAndGet();
        test.decrementAndGet();
        test.addAndGet();
        
    }
}
View Code

源代碼:

    private volatile int value;// 初始化值

    /**
     * 創建一個AtomicInteger,初始值value為initialValue
     */
    public AtomicInteger(int initialValue) {
        value = initialValue;
    }

    /**
     * 創建一個AtomicInteger,初始值value為0
     */
    public AtomicInteger() {
    }

    /**
     * 返回value
     */
    public final int get() {
        return value;
    }

    /**
     * 為value設值(基於value),而其他操作是基於舊值<--get()
     */
    public final void set(int newValue) {
        value = newValue;
    }

    public final boolean compareAndSet(int expect, int update) {
        return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
    }
    
    /**
     * 基於CAS為舊值設定新值,采用無限循環,直到設置成功為止
     * 
     * @return 返回舊值
     */
    public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();// 獲取當前值(舊值)
            if (compareAndSet(current, newValue))// CAS新值替代舊值
                return current;// 返回舊值
        }
    }

    /**
     * 當前值+1,采用無限循環,直到+1成功為止
     * @return the previous value 返回舊值
     */
    public final int getAndIncrement() {
        for (;;) {
            int current = get();//獲取當前值
            int next = current + 1;//當前值+1
            if (compareAndSet(current, next))//基於CAS賦值
                return current;
        }
    }

    /**
     * 當前值-1,采用無限循環,直到-1成功為止 
     * @return the previous value 返回舊值
     */
    public final int getAndDecrement() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

    /**
     * 當前值+delta,采用無限循環,直到+delta成功為止 
     * @return the previous value  返回舊值
     */
    public final int getAndAdd(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return current;
        }
    }

    /**
     * 當前值+1, 采用無限循環,直到+1成功為止
     * @return the updated value 返回新值
     */
    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;//返回新值
        }
    }

    /**
     * 當前值-1, 采用無限循環,直到-1成功為止 
     * @return the updated value 返回新值
     */
    public final int decrementAndGet() {
        for (;;) {
            int current = get();
            int next = current - 1;
            if (compareAndSet(current, next))
                return next;//返回新值
        }
    }

    /**
     * 當前值+delta,采用無限循環,直到+delta成功為止  
     * @return the updated value 返回新值
     */
    public final int addAndGet(int delta) {
        for (;;) {
            int current = get();
            int next = current + delta;
            if (compareAndSet(current, next))
                return next;//返回新值
        }
    }

    /**
     * 獲取當前值
     */
    public int intValue() {
        return get();
    }
View Code

說明:使用與源代碼都簡單到爆了!自己看看注釋。

注意:

  • value是volatile的,關於volatile的相關內容見《附2 volatile》,具體鏈接:http://www.cnblogs.com/java-zhao/p/5125698.html
  • 單步操作:例如set()是直接對value進行操作的,不需要CAS,因為單步操作就是原子操作。
  • 多步操作:例如getAndSet(int newValue)是兩步操作-->先獲取值,在設置值,所以需要原子化,這里采用CAS實現。
  • 對於方法是返回舊值還是新值,直接看方法是以get開頭(返回舊值)還是get結尾(返回新值)就好
  • CAS:比較CPU內存上的值是不是當前值current,如果是就換成新值update,如果不是,說明獲取值之后到設置值之前,該值已經被別人先一步設置過了,此時如果自己再設置值的話,需要在別人修改后的值的基礎上去操作,否則就會覆蓋別人的修改,所以這個時候會直接返回false,再進行無限循環,重新獲取當前值,然后再基於CAS進行加減操作。
  • 如果還是不懂CAS,類比數據庫的樂觀鎖

補充一個東西:

 1     // setup to use Unsafe.compareAndSwapInt for updates
 2     private static final Unsafe unsafe = Unsafe.getUnsafe();
 3     private static final long valueOffset;
 4 
 5     static {
 6         try {
 7             valueOffset = unsafe.objectFieldOffset
 8                 (AtomicInteger.class.getDeclaredField("value"));
 9         } catch (Exception ex) { throw new Error(ex); }
10     }
11 
12     private volatile int value;

這是AtomicInteger的所有屬性,其中value存的是當前值,而當前值存放的內存地址可以通過valueOffset來確定。實際上是“value字段相對Java對象的起始地址的偏移量”

1     public final boolean compareAndSet(int expect, int update) {
2         return unsafe.compareAndSwapInt(this, valueOffset, expect, update);
3     }

CAS方法:通過對比“valueOffset上的value”與expect是否相同,來決定是否修改value值為update值。

 

ABA 問題:執行流程如下

int i=100;

Thread1: 100->101->100

Thread2:                 100-> 200

Thread2沒有發現Thread1的中間i發生了變化的過程。

ABA問題怎么解決?

第一種姿勢:不處理,對於只關注最后的結果,不關注中間變化過程,ABA 無所謂,例如 AtomicInteger.compareAndset(...)

第二種姿勢:使用版本號控制,eg. AtomicStampedReference 提供一個版本號,過程如下

Thread1: 100 1->101 2->100 3(前邊為數據,空格后是版本號)

Thread2:                      100 1-> 200(此時報錯,版本號不一致)

那版本號是否也會出現ABA問題,只要后續的版本號不會出現之前的版本號就行(eg. 遞增)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM