国产探花免费观看_亚洲丰满少妇自慰呻吟_97日韩有码在线_资源在线日韩欧美_一区二区精品毛片,辰东完美世界有声小说,欢乐颂第一季,yy玄幻小说排行榜完本

首頁 > 學院 > 開發設計 > 正文

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

2019-11-14 23:14:50
字體:
來源:轉載
供稿:網友
java并發包源碼學習之AQS框架(二)CLH lock queue和自旋鎖

上一篇文章提到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源碼分析中做詳細介紹。


發表評論 共有條評論
用戶名: 密碼:
驗證碼: 匿名發表
主站蜘蛛池模板: 浙江省| 台南县| 潼南县| 高陵县| 青川县| 广水市| 五峰| 大新县| 来安县| 阳山县| 辛集市| 图们市| 荣昌县| 襄樊市| 郸城县| 长宁区| 秦皇岛市| 洪洞县| 奈曼旗| 沁阳市| 凤台县| 乌鲁木齐市| 光山县| 灵寿县| 忻州市| 太康县| 博爱县| 滕州市| 安龙县| 延寿县| 扬州市| 长汀县| 来安县| 铁岭县| 容城县| 浦县| 三江| 宁德市| 鹤山市| 绿春县| 景泰县|