AtomicInteger 源碼 及部分問題

  今天看了下傳說中線程安全的AtomicInteger的源碼,總共只有不到300行,其中關鍵的代碼就更少了,主要有以下幾段:安全

  1.value值設爲volatile,保證其內存可見性ide

 private volatile int value;

  值得注意的是,volatile關鍵字只是一種輕量級的同步方法,用來保證變量的內存可見性,可是並不能代替synchronizied。簡單舉個例子:測試

  

static volatile int count = 0;
    
    public static void main(String[] args) {
        
        for(int i=0;i<10000;i++){
            new Thread(new Runnable() {
                
                @Override
                public void run() {
                    count++;
                }
            }).start();
        }
        
        System.out.println("count:"+count);

  最終輸出結果並不必定是指望的10000,而每每是一個比10000小的數字。緣由是可能多個線程同時從主內存中讀取到最新的conut值,而後在各自的工做內存中執行了++操做,再往主存中同步的時候,就會寫入一樣的值,至關於一次++操做失效。this

  

  2.接着看源碼,getAndSet(int newValue)方法spa

  /**
     * Atomically sets to the given value and returns the old value.
     *
     * @param newValue the new value
     * @return the previous value
     */
    public final int getAndSet(int newValue) {
        for (;;) {
            int current = get();
            if (compareAndSet(current, newValue))
                return current;
        }
    }

  方法中並無synchronizied或者volatile關鍵字,那到底是如何實現線程安全的呢?關鍵的方法在於compareAndSet(current,newValue),源碼以下:線程

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

  調用了unsafe的compareAndSwapInt方法,是利用了大名鼎鼎的CAS來實現的原子操做,雖然CAS也用到了排他鎖,可是比起synchronized,效率要高的多。code

  3.getAndIncrement()方法blog

  /**
     * Atomically increments by one the current value.
     *
     * @return the previous value
     */
    public final int getAndIncrement() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return current;
        }
    }

  可見跟getAndSet方法大同小異,首先get()取出當前的value值,而後+1,緊接着仍是調用了compareAndSet方法,看next和預期值是否相同,不相同則進入下一輪循環。內存

  4.incrementAndGet()方法rem

    /**
     * Atomically increments by one the current value.
     *
     * @return the updated value
     */
    public final int incrementAndGet() {
        for (;;) {
            int current = get();
            int next = current + 1;
            if (compareAndSet(current, next))
                return next;
        }
    }

  與getAndIncrement相比,只是最後一行代碼有差異,一個是返回current,一個是返回next,我的理解就是利用CAS的原子操做,實現了線程安全的 i++和 ++i 。

  其餘的方法也都是相似的,到底層調用unsafe包的CAS原子操做實現,很少說了,下面是作了一個測試:

public static void main(String[] args) {

        final AtomicInteger integer = new AtomicInteger(0);

        for (int i = 0; i < 10000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    integer.getAndIncrement();
                }
            }).start();
        }

        System.out.println(integer.get());

 

 

  運行,輸出10000沒問題,再運行幾遍,9999,9998都出現了,什麼狀況?說好的線程安全呢?百思不得其解,最後求助大神,給出答案,原來循環中的線程尚未執行完,就執行了打印的語句。修改代碼以下:

public static void main(String[] args) {

        final AtomicInteger integer = new AtomicInteger(0);

        for (int i = 0; i < 10000; i++) {
            new Thread(new Runnable() {
                @Override
                public void run() {
                    integer.getAndIncrement();
                }
            }).start();
        }

        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        System.out.println(integer.get());

    }

  OK,再也沒有出現以前的狀況,可見AtomicInteger確實是能保證線程安全的,不過因爲是一個無限的for循環裏面,不斷的進行比較、更新操做,所以,能夠想象,在線程數量很是大的狀況下,運行速度會比較慢。

相關文章
相關標籤/搜索