Java並發包源碼學習之AQS架構(二)CLH lock queue和自旋鎖

來源:互聯網
上載者:User

標籤:

上一篇文章提到AQS是基於CLH lock queue,那麼什麼是CLH lock queue,說複雜很複雜說簡單也簡單, 所謂大道至簡:

CLH lock queue其實就是一個FIFO的隊列,隊列中的每個結點(線程)只要等待其前繼釋放鎖就可以了。

AbstractQueuedSynchronizer是通過一個內部類Node來實現CLH lock queue的一個變種,但基本原理是類似的。

在介紹Node類之前,我們來介紹下Spin Lock,通常就是用CLH lock queue來實現自旋鎖,所謂自旋鎖簡單來說就是線程通過迴圈來等待而不是睡眠。 Talk 再多不如 show code:

class ClhSpinLock {    private final ThreadLocal<Node> prev;    private final ThreadLocal<Node> node;    private final AtomicReference<Node> tail = new AtomicReference<Node>(new Node());    public ClhSpinLock() {        this.node = new ThreadLocal<Node>() {            protected Node initialValue() {                return new Node();            }        };        this.prev = new ThreadLocal<Node>() {            protected Node initialValue() {                return null;            }        };    }    public void lock() {        final Node node = this.node.get();        node.locked = true;        // 一個CAS操作即可將當前線程對應的節點加入到隊列中,        // 並且同時獲得了前繼節點的引用,然後就是等待前繼釋放鎖        Node pred = this.tail.getAndSet(node);        this.prev.set(pred);        while (pred.locked) {// 進入自旋        }    }    public void unlock() {        final Node node = this.node.get();        node.locked = false;        this.node.set(this.prev.get());    }    private static class Node {        private volatile boolean locked;    }}

上面的代碼中線程巧妙的通過ThreadLocal儲存了當前結點和前繼結點的引用,自旋就是lock中的while迴圈。 總的來說這種實現的好處是保證所有等待線程的公平競爭,而且沒有競爭同一個變數,因為每個線程只要等待自己的前繼釋放就好了。 而自旋的好處是線程不需要睡眠和喚醒,減小了系統調用的開銷。

public static void main(String[] args) {    final ClhSpinLock lock = new ClhSpinLock();    lock.lock();    for (int i = 0; i < 10; i++) {        new Thread(new Runnable() {            @Override            public void run() {                lock.lock();                System.out.println(Thread.currentThread().getId() + " acquired the lock!");                lock.unlock();            }        }).start();        Thread.sleep(100);    }    System.out.println("main thread unlock!");    lock.unlock();}

上面代碼的啟動並執行結果應該跟上一篇文章中的完全一樣。

ClhSpinLock的Node類實現很簡單只有一個布爾值,AbstractQueuedSynchronizer$Node的實現稍微複雜點,大概是這樣的:

     +------+  prev +-----+       +-----+head |      | <---- |     | <---- |     |  tail     +------+       +-----+       +-----+
  • head:頭指標
  • tail:尾指標
  • prev:指向前繼的指標
  • next:這個指標圖中沒有畫出來,它跟prev相反,指向後繼

關鍵不同就是next指標,這是因為AQS中線程不是一直在自旋的,而可能會反覆的睡眠和喚醒,這就需要前繼釋放鎖的時候通過next 指標找到其後繼將其喚醒,也就是AQS的等待隊列中後繼是被前繼喚醒的。AQS結合了自旋和睡眠/喚醒兩種方法的優點。

其中線程的睡眠和喚醒就是用到我下一篇文章將要講到的LockSupport

最後提一點,上面的ClhSpinLock類中還有一個關鍵的點就是lock方法中注釋的地方:

一個CAS操作即可將當前線程對應的節點加入到隊列中,並擷取到其前繼。

實際上可以說整個AQS架構都是建立在CAS的基礎上的,這些原子操作是多線程競爭的核心地帶,AQS中很多繞來繞去的代碼都是為了 減少競爭。我會在後面AbstractQueuedSynchronizer源碼分析中做詳細介紹。

 

Java並發包源碼學習之AQS架構(二)CLH lock queue和自旋鎖

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.