1. 程式人生 > >Java併發包原始碼學習之AQS框架(二)CLH lock queue和自旋鎖

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原始碼分析中做詳細介紹。

相關推薦

Java併發原始碼學習AQS框架CLH lock queue

上一篇文章提到AQS是基於CLH lock queue,那麼什麼是CLH lock queue,說複雜很複雜說簡單也簡單, 所謂大道至簡: CLH lock queue其實就是一個FIFO的佇列,佇列中的每個結點(執行緒)只要等待其前繼釋放鎖就可以了。 AbstractQueuedSynchronizer

Java併發原始碼學習AQS框架AbstractQueuedSynchronizer原始碼分析

經過前面幾篇文章的鋪墊,今天我們終於要看看AQS的廬山真面目了,建議第一次看AbstractQueuedSynchronizer 類原始碼的朋友可以先看下我前面幾篇文章: 分析原始碼是非常枯燥乏味的一件事,其實程式碼本身其實就是最好的說明了,因此基本都是貼出一些程式碼加上一些註釋, 因為Abstract

Java併發原始碼學習AQS框架概述

AQS其實就是java.util.concurrent.locks.AbstractQueuedSynchronizer這個類。 閱讀Java的併發包原始碼你會發現這個類是整個java.util.concurrent的核心之一,也可以說是閱讀整個併發包原始碼的一個突破口。 比如讀ReentrantLock的

Java併發原始碼學習執行緒池ThreadPoolExecutor原始碼分析

Java中使用執行緒池技術一般都是使用Executors這個工廠類,它提供了非常簡單方法來建立各種型別的執行緒池: public static ExecutorService newFixedThreadPool(int nThreads) public static ExecutorService

JavaWeb學習Hibernate框架

utils xtend auto etl SQ dial begin 可選 oct hibernateAPI詳解 Configuration 創建 加載主配置 創建sessionFactory

Java併發原始碼學習系列:AQS共享式與獨佔式獲取與釋放資源的區別

[toc] # Java併發包原始碼學習系列:AQS共享模式獲取與釋放資源 往期回顧: - [Java併發包原始碼學習系列:AbstractQueuedSynchronizer](https://www.cnblogs.com/summerday152/p/14238284.html) - [Java併

Java併發原始碼學習系列:阻塞佇列實現ArrayBlockingQueue原始碼解析

[toc] 系列傳送門: - [Java併發包原始碼學習系列:AbstractQueuedSynchronizer](https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/112254373) - [Java併發包原始碼學習系列:CLH同步佇列及同步資源

Java併發原始碼學習系列:阻塞佇列實現LinkedBlockingQueue原始碼解析

[toc] 系列傳送門: - [Java併發包原始碼學習系列:AbstractQueuedSynchronizer](https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/112254373) - [Java併發包原始碼學習系列:CLH同步佇列及同步資源

Java併發原始碼學習系列:阻塞佇列實現PriorityBlockingQueue原始碼解析

[toc] 系列傳送門: - [Java併發包原始碼學習系列:AbstractQueuedSynchronizer](https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/112254373) - [Java併發包原始碼學習系列:CLH同步佇列及同步資源

Java併發原始碼學習系列:阻塞佇列實現DelayQueue原始碼解析

[toc] 系列傳送門: - [Java併發包原始碼學習系列:AbstractQueuedSynchronizer](https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/112254373) - [Java併發包原始碼學習系列:CLH同步佇列及同步資源

Java併發原始碼學習系列:阻塞佇列實現SynchronousQueue原始碼解析

[toc] 系列傳送門: - [Java併發包原始碼學習系列:AbstractQueuedSynchronizer](https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/112254373) - [Java併發包原始碼學習系列:CLH同步佇列及同步資源

Java併發原始碼學習系列:阻塞佇列實現LinkedTransferQueue原始碼解析

[toc] 系列傳送門: Java併發包原始碼學習系列:AbstractQueuedSynchronizer Java併發包原始碼學習系列:CLH同步佇列及同步資源獲取與釋放 Java併發包原始碼學習系列:AQS共享式與獨佔式獲取與釋放資源的區別 Java併發包原始碼學習系列:ReentrantLock可重

Java併發原始碼學習系列:AbstractQueuedSynchronizer

[toc] > 本文基於JDK1.8 ## 本篇學習目標 - 瞭解AQS的設計思想以及重要欄位含義,如通過state欄位表示同步狀態等。 - 瞭解AQS內部維護鏈式雙向同步佇列的結構以及幾個重要指標。 - 瞭解五種重要的同步狀態。 - 明確兩種模式:共享模式和獨佔模式。 - 學習兩種模式下AQS提供的模

Java併發原始碼學習系列:CLH同步佇列及同步資源獲取與釋放

[toc] ## 本篇學習目標 - 回顧CLH同步佇列的結構。 - 學習獨佔式資源獲取和釋放的流程。 ## CLH佇列的結構 我在[Java併發包原始碼學習系列:AbstractQueuedSynchronizer#同步佇列與Node節點](https://www.cnblogs.com/summer

Java併發原始碼學習系列:掛起與喚醒執行緒LockSupport工具類

[toc] 系列傳送門: - [Java併發包原始碼學習系列:AbstractQueuedSynchronizer](https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/112254373) - [Java併發包原始碼學習系列:CLH同步佇列及同步資源

Java併發原始碼學習系列:JDK1.8的ConcurrentHashMap原始碼解析

[toc] 系列傳送門: - [Java併發包原始碼學習系列:AbstractQueuedSynchronizer](https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/112254373) - [Java併發包原始碼學習系列:CLH同步佇列及同步資源

Java併發原始碼學習系列:阻塞佇列BlockingQueue及實現原理分析

[toc] 系列傳送門: - [Java併發包原始碼學習系列:AbstractQueuedSynchronizer](https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/112254373) - [Java併發包原始碼學習系列:CLH同步佇列及同步資源

Java併發原始碼學習系列:基於CAS非阻塞併發佇列ConcurrentLinkedQueue原始碼解析

[toc] ## 非阻塞併發佇列ConcurrentLinkedQueue概述 我們之前花了很多時間瞭解學習BlockingQueue阻塞佇列介面下的各種實現,也大概對阻塞佇列的實現機制有了一定的瞭解:阻塞 + 佇列嘛。 而且其中絕大部分是完全基於獨佔鎖ReentrantLock和條件機制conditi

Java併發原始碼學習系列:執行緒池ThreadPoolExecutor原始碼解析

[toc] 系列傳送門: - [Java併發包原始碼學習系列:AbstractQueuedSynchronizer](https://blog.csdn.net/Sky_QiaoBa_Sum/article/details/112254373) - [Java併發包原始碼學習系列:CLH同步佇列及同步資源

Java併發原始碼學習系列:執行緒池ScheduledThreadPoolExecutor原始碼解析

[toc] ## ScheduledThreadPoolExecutor概述 我們在上一篇學習了ThreadPoolExecutor的實現原理:[Java併發包原始碼學習系列:執行緒池ThreadPoolExecutor原始碼解析](https://blog.csdn.net/Sky_QiaoBa_Sum