java並發編程(8)原子變數和非阻塞的同步機制,java變數

來源:互聯網
上載者:User

java並發編程(8)原子變數和非阻塞的同步機制,java變數
原子變數和非阻塞的同步機制 一、鎖的劣勢

  1.在多線程下:鎖的掛起和恢複等過程存在著很大的開銷(及時現代的jvm會判斷何時使用掛起,何時自旋等待)

  2.volatile:輕量層級的同步機制,但是不能用於構建原子複合操作

  因此:需要有一種方式,在管理線程之間的競爭時有一種粒度更細的方式,類似與volatile的機制,同時還要支援原子更新操作

二、CAS

  獨佔鎖是一種悲觀的技術--它假設最壞的情況,所以每個線程是獨佔的

  而CAS比較並交換:compareAndSwap/Set(A,B):我們認為記憶體處值是A,如果是A,將其修改為B,否則不進行操作;返回記憶體處的原始值或是否修改成功

  如:類比CAS操作 

//類比的CASpublic class SimulatedCAS {    private int value;    public synchronized int get() {        return value;    }    //CAS操作    public synchronized int compareAndSwap(int expectedValue, int newValue) {        int oldValue = value;        if (oldValue == expectedValue) {            value = newValue;        }        return oldValue;    }    public synchronized boolean compareAndSet(int expectedValue, int newValue) {        return (expectedValue == compareAndSwap(expectedValue, newValue));    }}//典型使用情境public class CasCounter {    private SimulatedCAS value;    public int getValue() {        return value.get();    }    public int increment() {        int v;        do {            v = value.get();        } while {            (v != value.compareAndSwap(v, v + 1));        }        return v + 1;    }}

 

  JAVA提供了CAS的操作

    原子狀態類:AtomicXXX的CAS方法

    JAVA7/8:對Map的操作:putIfAbsent、computerIfAbsent、computerIfPresent.........

三、原子變數類

   AtomicRefence原子更新對象,可以是自訂的對象;如:

public class CasNumberRange {    private static class IntPair {        // INVARIANT: lower <= upper        final int lower;        //將值定義為不可變域        final int upper;        //將值定義為不可變域        public IntPair(int lower, int upper) {            this.lower = lower;            this.upper = upper;        }    }    private final AtomicReference<IntPair> values = new AtomicReference<IntPair>(new IntPair(0, 0));    //封裝對象    public int getLower() {        return values.get().lower;    }    public int getUpper() {        return values.get().upper;    }    public void setLower(int i) {        while (true) {            IntPair oldv = values.get();            if (i > oldv.upper) {                throw new IllegalArgumentException("Can't set lower to " + i + " > upper");            }            IntPair newv = new IntPair(i, oldv.upper);  //屬性為不可變域,則每次更新建立對象            if (values.compareAndSet(oldv, newv)) {     //原子更新,如果在過程中有線程修改了,則其他線程不會更新成功,因為oldv與記憶體處值就不同了                return;            }        }    }    //同上    public void setUpper(int i) {        while (true) {            IntPair oldv = values.get();            if (i < oldv.lower)                throw new IllegalArgumentException("Can't set upper to " + i + " < lower");            IntPair newv = new IntPair(oldv.lower, i);            if (values.compareAndSet(oldv, newv))                return;        }    }}

 

  效能問題:使用原子變數在中低並發(競爭)下,比使用鎖速度要快,一般情況下是比鎖速度快的

 

四、非阻塞演算法

  許多常見的資料結構中都可以使用非阻塞演算法

  非阻塞演算法:在多線程中,工作是否成功有不確定性,需要迴圈執行,並通過CAS進行原子操作

  1、上面的CasNumberRange

  2、棧的非阻塞演算法:只儲存頭部指標,只有一個狀態

//棧實現的非阻塞演算法:單向鏈表public class ConcurrentStack <E> {    AtomicReference<Node<E>> top = new AtomicReference<Node<E>>();    public void push(E item) {        Node<E> newHead = new Node<E>(item);        Node<E> oldHead;        do {            oldHead = top.get();            newHead.next = oldHead;        } while (!top.compareAndSet(oldHead, newHead));//CAS操作:原子更新操作,迴圈判斷,非阻塞    }    public E pop() {        Node<E> oldHead;        Node<E> newHead;        do {            oldHead = top.get();            if (oldHead == null) {                return null;            }            newHead = oldHead.next;        } while (!top.compareAndSet(oldHead, newHead));//CAS操作:原子更新操作,迴圈判斷,非阻塞        return oldHead.item;    }    private static class Node <E> {        public final E item;        public Node<E> next;        public Node(E item) {            this.item = item;        }    }}

 

  3、鏈表的非阻塞演算法:頭部和尾部的快速存取,儲存兩個狀態,更加複雜

public class LinkedQueue <E> {    private static class Node <E> {        final E item;        final AtomicReference<LinkedQueue.Node<E>> next;        public Node(E item, LinkedQueue.Node<E> next) {            this.item = item;            this.next = new AtomicReference<LinkedQueue.Node<E>>(next);        }    }    private final LinkedQueue.Node<E> dummy = new LinkedQueue.Node<E>(null, null);    private final AtomicReference<LinkedQueue.Node<E>> head = new AtomicReference<LinkedQueue.Node<E>>(dummy);    private final AtomicReference<LinkedQueue.Node<E>> tail = new AtomicReference<LinkedQueue.Node<E>>(dummy);  //儲存尾節點    public boolean put(E item) {        LinkedQueue.Node<E> newNode = new LinkedQueue.Node<E>(item, null);        while (true) {            LinkedQueue.Node<E> curTail = tail.get();            LinkedQueue.Node<E> tailNext = curTail.next.get();            if (curTail == tail.get()) {                if (tailNext != null) {                    // 處於中間狀態,更新尾節點為當前尾節點的next                    tail.compareAndSet(curTail, tailNext);                } else {                    // 將當前尾節點的next 設定為新節點:鏈表                    if (curTail.next.compareAndSet(null, newNode)) {                        /**                         * 此處即為中間狀態,雖然在這裡進行了兩次原子操作,整體不是原子的,但是通過演算法保證了安全:                         * 原因是處於中間狀態時,如果有其他線程進來操作,則上面那個if將執行;                         * 上面if的操作是來協助當前線程完成更新尾節點操作,而當前線程的更新就會失敗返回,最終則是更新成功                         */                                                // 連結成功,尾節點已經改變,則將當前尾節點,設定為新節點                        tail.compareAndSet(curTail, newNode);                        return true;                    }                }            }        }    }}

 

  3.原子域更新器

    上面的邏輯,實現了鏈表的非阻塞演算法,使用Node來儲存頭結點和尾節點

    在實際的ConcurrentLinkedQueue中使用的是基於反射的AtomicReferenceFiledUpdater來封裝Node

五、ABA問題

  CAS操作中容易出現的問題:

    判斷值是否為A,是的話就繼續更新操作換為B;

    但是如果一個線程將值A改為C,然後又改回A,此時,原線程將判斷A=A成功執行更新操作;

    如果把A改為C,然後又改回A的操作,也需要視為變化,則需要對演算法進行最佳化

  解決:添加版本號碼,每次更新操作都要更新版本號碼,即使值是一樣的

      

 

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.