日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程语言 > java >内容正文

java

Java 并发编程—有锁互斥机制及AQS理论

發布時間:2024/4/15 java 45 豆豆
生活随笔 收集整理的這篇文章主要介紹了 Java 并发编程—有锁互斥机制及AQS理论 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

原文作者:Java并發編程

原文地址:AQS這樣學就很簡單了

目錄

一、有鎖互斥機制

二、AQS如何實現互斥

三、結語


如果你是道格李,你要實現一套機制來保證線程互斥,你會如何實現呢?你肯定不會一上來就寫代碼對吧,你會想有哪些場景會出現線程互斥、針對每個場景抽象出需要實現的功能、針對這些功能底層選擇什么樣的數據結構什么算法……這其實是一種非常好的學習方法,回歸到問題本身去思考問題,然后帶著問題去找答案,而不是一上來就一頭扎進代碼中。

一、有鎖互斥機制

1、核心功能

如果你要實現一套機制保證線程互斥,核心是實現這六個功能:搶鎖、釋放鎖、入隊、出隊、阻塞、喚醒。下面分別說下這六個功能如何實現以及在實現的時候需要考慮哪些特殊情況。

2、搶鎖

如果當前沒有任何線程持有鎖,那進來的線程就可以去搶鎖。因為存在多個線程同時上鎖,所以需要通過機制來保證同一時刻只能有一個線程能夠操作鎖標志位,這個機制就是CAS。如果有線程持有了鎖,這時候進來的線程如果是持有鎖的線程,就發生了重入,需要記錄重入次數,因為解鎖的時候也要解鎖相應的次數;如果不是持有鎖的線程,那有兩個選擇:讓線程運行結束、讓線程阻塞等其他線程用完鎖再喚醒運行。顯然,讓線程運行結束不合適,因為線程該執行的任務還未執行。正確的做法肯定是先把這個線程保存下來,然后讓線程阻塞。待持有鎖的線程運行結束再喚醒執行。這里就是公平鎖與非公平鎖的唯一差異所在,一個臨界點。

3、釋放鎖

持有鎖的線程將該執行的任務執行完就得釋放鎖。當然肯定不是這么簡單,還得考慮鎖重入、線程喚醒。

4、入隊

未搶到鎖且不是重入的線程就需要被阻塞等待喚醒,那喚醒的時候去哪找這個線程呢?很明顯這些線程滿足先阻塞先喚醒原則,與數據結構隊列的FIFO特性相吻合。所以在實現的時候需要借助隊列,將這部分線程封裝成節點入隊。入隊就得考慮從頭部入隊還是從尾部入隊。如果隊列中還沒有數據,隊列的頭尾兩個節點都指向這個節點。如果隊列中有數據,就從尾部插入。大家在看AQS源碼的時候會發現AQS不是這樣做的,它會多一個節點。AQS隊列的頭結點永遠是當前持有鎖的線程占用,為什么要這樣做呢?這就需要對CPU內部結構及工作機制有深入理解,感興趣的同學可以深入學習下:CLHLock、MCSLock

5、出隊

線程對應的節點何時出隊呢?肯定是喚醒以后。那由誰來操作出隊呢?是喚醒你的線程還是你自己?可想而知,由自己來操作更合理。那自己什么時候操作出隊呢?肯定是喚醒以后。只有理解了這個邏輯,你才能看得懂AQS中阻塞那塊的代碼為什么那樣寫。正常的情況下出隊都是從頭部出,但是有特殊情況會移除隊列中部的節點或尾部節點。

6、阻塞

線程如果沒有搶到鎖依然在那嘗試搶鎖這就是所謂的自旋鎖,很顯然,這樣很浪費資源,肯定沒有搶到鎖的線程執行完任務喚醒高效。那如何不占用資源呢?就是阻塞自己,讓出資源,不被調度。

7、喚醒

未搶到鎖的線程為了不占用資源阻塞了自己,拿到鎖的線程執行完任務需要來喚醒,不然就會出現奇怪的現象:搶到鎖的線程執行完任務退出了,未搶到鎖的線程全部阻塞在那里等待喚醒。

二、AQS如何實現互斥

別看AQS代碼挺多,其實搞懂這三個機制,代碼理解起來就非常easy。

1、state

這個屬性就是鎖標志位。如果有線程搶到了鎖,這個值就會通過CAS改成1。何時改回來呢?持有鎖的線程執行完任務釋放鎖的時候。

2、waitStatus

這個屬性有五個值:0,-1,-2,-3,1。這里介紹其中三個。其他的值跟互斥沒關系,我會在講到相關的技術時講到。這幾個值直接的轉換后面會寫文章細致,本篇文章大致講下。未搶到鎖的線程被封裝成Node插入隊列,這個屬性默認值為0。插入隊列后會進行兩次自旋,如果都沒搶到鎖,就會將它的前置節點的waitStatus改為-1。注意一下這里,改的不是自己的waitStatus,而是它前置節點的。為什么要這樣做呢?這就是自旋鎖算法CLH的理論。相當于在它的前置節點上設置了一個鬧鐘,這樣在喚醒的時候就不需要去隊列取數據,直接判斷自己的該屬性就可以了。如果是隊列中的第二個節點但是搶鎖失敗了,這個時候就將自己的waitStatus設置為1。這樣的線程就得不到調度機會了,會被其他線程從隊列中移除。

3、隊列

如果想看懂AQS源碼,必須對隊列的相關操作算法非常熟悉,比如初始化、判空、入隊、出隊……建議在看AQS源碼之前自己用Java實現一遍隊列。

三、結語

這篇文章雖然沒有講任何AQS的源碼,但是如果你真的看懂了這篇文章,你去讀讀AQS的源碼,你會發現讀起來非常輕松。推薦閱讀Java并發編程—AQS原理分析

總結

以上是生活随笔為你收集整理的Java 并发编程—有锁互斥机制及AQS理论的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。