欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Java并發(fā)系列之AbstractQueuedSynchronizer源碼分析(概要分析)

 更新時間:2018年02月27日 10:30:14   作者:勞夫子  
這篇文章主要為大家詳細介紹了Java并發(fā)系列之AbstractQueuedSynchronizer源碼,具有一定的參考價值,感興趣的小伙伴們可以參考一下

學(xué)習(xí)Java并發(fā)編程不得不去了解一下java.util.concurrent這個包,這個包下面有許多我們經(jīng)常用到的并發(fā)工具類,例如:ReentrantLock, CountDownLatch, CyclicBarrier, Semaphore等。而這些類的底層實現(xiàn)都依賴于AbstractQueuedSynchronizer這個類,由此可見這個類的重要性。所以在Java并發(fā)系列文章中我首先對AbstractQueuedSynchronizer這個類進行分析,由于這個類比較重要,而且代碼比較長,為了盡可能分析的透徹一些,我決定用四篇文章對該類進行一個比較完整的介紹。本篇文章作為概要介紹主要是讓讀者們對該類有個初步了解。為了敘述簡單,后續(xù)有些地方會用AQS代表這個類。

1. AbstractQueuedSynchronizer這個類是干嘛的?

相信要許多讀者使用過ReentrantLock,但是卻不知道AbstractQueuedSynchronizer的存在。其實ReentrantLock實現(xiàn)了一個內(nèi)部類Sync,該內(nèi)部類繼承了AbstractQueuedSynchronizer,所有鎖機制的實現(xiàn)都是依賴于Sync內(nèi)部類,也可以說ReentrantLock的實現(xiàn)就是依賴于AbstractQueuedSynchronizer類。于此類似,CountDownLatch, CyclicBarrier, Semaphore這些類也是采用同樣的方式來實現(xiàn)自己對于鎖的控制。可見,AbstractQueuedSynchronizer是這些類的基石。那么AQS內(nèi)部到底實現(xiàn)了什么以至于所以這些類都要依賴于它呢?可以這樣說,AQS為這些類提供了基礎(chǔ)設(shè)施,也就是提供了一個密碼鎖,這些類擁有了密碼鎖之后可以自己來設(shè)置密碼鎖的密碼。此外,AQS還提供了一個排隊區(qū),并且提供了一個線程訓(xùn)導(dǎo)員,我們知道線程就像一個原始的野蠻人,它不懂得講禮貌,它只會橫沖直撞,所以你得一步一步去教它,告訴它什么時候需要去排隊了,要到哪里去排隊,排隊前要做些什么,排隊后要做些什么。這些教化工作全部都由AQS幫你完成了,從它這里教化出來的線程都變的非常文明懂禮貌,不再是原始的野蠻人,所以以后我們只需要和這些文明的線程打交道就行了,千萬不要和原始線程有過多的接觸!

2. 為何說AbstractQueuedSynchronizer提供了一把密碼鎖?

//同步隊列的頭結(jié)點
private transient volatile Node head; 

//同步隊列的尾結(jié)點
private transient volatile Node tail;

//同步狀態(tài)
private volatile int state;

//獲取同步狀態(tài)
protected final int getState() {
 return state;
}

//設(shè)置同步狀態(tài)
protected final void setState(int newState) {
 state = newState;
}

//以CAS方式設(shè)置同步狀態(tài)
protected final boolean compareAndSetState(int expect, int update) {
 return unsafe.compareAndSwapInt(this, stateOffset, expect, update);
}

上面的代碼列出了AQS的所有成員變量,可以看到AQS的成員變量只有三個,分別是同步隊列頭結(jié)點引用,同步隊列尾結(jié)點引用以及同步狀態(tài)。注意,這三個成員變量都使用了volatile關(guān)鍵字進行修飾,這就確保了多個線程對它的修改都是內(nèi)存可見的。整個類的核心就是這個同步狀態(tài),可以看到同步狀態(tài)其實就是一個int型的變量,大家可以把這個同步狀態(tài)看成一個密碼鎖,而且還是從房間里面鎖起來的密碼鎖,state具體的值就相當(dāng)于密碼控制著密碼鎖的開合。當(dāng)然這個鎖的密碼是多少就由各個子類來規(guī)定了,例如在ReentrantLock中,state等于0表示鎖是開的,state大于0表示鎖是鎖著的,而在Semaphore中,state大于0表示鎖是開的,state等于0表示鎖是鎖著的。

3. AbstractQueuedSynchronizer的排隊區(qū)是怎樣實現(xiàn)的?

AbstractQueuedSynchronizer內(nèi)部其實有兩個排隊區(qū),一個是同步隊列,一個是條件隊列。從上圖可以看出,同步隊列只有一條,而條件隊列可以有多條。同步隊列的結(jié)點分別持有前后結(jié)點的引用,而條件隊列的結(jié)點只有一個指向后繼結(jié)點的引用。圖中T表示線程,每個結(jié)點包含一個線程,線程在獲取鎖失敗后首先進入同步隊列排隊,而想要進入條件隊列該線程必須持有鎖才行。接下來我們看看隊列中每個結(jié)點的結(jié)構(gòu)。

//同步隊列的結(jié)點
static final class Node {
 
 static final Node SHARED = new Node(); //表示當(dāng)前線程以共享模式持有鎖
 
 static final Node EXCLUSIVE = null; //表示當(dāng)前線程以獨占模式持有鎖

 static final int CANCELLED = 1;  //表示當(dāng)前結(jié)點已經(jīng)取消獲取鎖
 
 static final int SIGNAL = -1;  //表示后繼結(jié)點的線程需要運行
 
 static final int CONDITION = -2;  //表示當(dāng)前結(jié)點在條件隊列中排隊
 
 static final int PROPAGATE = -3;  //表示后繼結(jié)點可以直接獲取鎖

 volatile int waitStatus; //表示當(dāng)前結(jié)點的等待狀態(tài)
 
 volatile Node prev;  //表示同步隊列中的前繼結(jié)點

 volatile Node next;  //表示同步隊列中的后繼結(jié)點 

 volatile Thread thread; //當(dāng)前結(jié)點持有的線程引用
 
 Node nextWaiter;   //表示條件隊列中的后繼結(jié)點

 //當(dāng)前結(jié)點狀態(tài)是否是共享模式
 final boolean isShared() {
  return nextWaiter == SHARED;
 }

 //返回當(dāng)前結(jié)點的前繼結(jié)點
 final Node predecessor() throws NullPointerException {
  Node p = prev;
  if (p == null) {
   throw new NullPointerException();
  } else {
   return p;
  }
 }
 
 //構(gòu)造器1
 Node() {}
 
 //構(gòu)造器2, 默認用這個構(gòu)造器
 Node(Thread thread, Node mode) {
  //注意持有模式是賦值給nextWaiter
  this.nextWaiter = mode;
  this.thread = thread;
 }
 
 //構(gòu)造器3, 只在條件隊列中用到
 Node(Thread thread, int waitStatus) {
  this.waitStatus = waitStatus;
  this.thread = thread;
 }
}

Node代表同步隊列和條件隊列中的一個結(jié)點,它是AbstractQueuedSynchronizer的內(nèi)部類。Node有很多屬性,比如持有模式,等待狀態(tài),同步隊列中的前繼和后繼,以及條件隊列中的后繼引用等等??梢园淹疥犃泻蜅l件隊列看成是排隊區(qū),每個結(jié)點看成是排隊區(qū)的座位,將線程看成是排隊的客人??腿藙倎頃r會先去敲敲門,看看鎖有沒有開,如果鎖沒開它就會去排隊區(qū)領(lǐng)取一個號碼牌,聲明自己想要以什么樣的方式來持有鎖,最后再到隊列的末尾進行排隊。

4 怎樣理解獨占模式和共享模式?

前面講到每個客人在排隊前會領(lǐng)取一個號碼牌,聲明自己想要以什么樣的方式來占有鎖,占有鎖的方式分為獨占模式和共享模式,那么怎樣來理解獨占模式和共享模式呢?實在找不到什么好的比喻,大家可以聯(lián)想一下公共廁所,獨占模式的人比較霸道,老子要么就不進,進來了就不許別人再進了,自己一個人獨自占用整個廁所。共享模式的人就沒那么講究了,當(dāng)它發(fā)現(xiàn)這個廁所已經(jīng)可以用了之后,它自己進來還不算,還得熱心的問下后面的人介不介意一起用,如果后面的人不介意一起使用那就不用再排隊了大家一起上就是了, 當(dāng)然如果后面的人介意那就只好留在隊列里繼續(xù)排隊了。

5 怎樣理解結(jié)點的等待狀態(tài)?

我們還看到每個結(jié)點都有一個等待狀態(tài),這個等待狀態(tài)分為CANCELLED,SIGNAL,CONDITION,PROPAGATE四種狀態(tài)??梢詫⑦@個等待狀態(tài)看作是掛在座位旁邊的牌子,標識當(dāng)前座位上的人的等待狀態(tài)。這個牌子的狀態(tài)不僅自己可以修改,其他人也可以修改。例如當(dāng)這個線程在排隊過程中已經(jīng)打算放棄了,它就會將自己座位上的牌子設(shè)置為CANCELLED,這樣其他人看到了就可以將它清理出隊列。還有一種情況是,當(dāng)線程在座位上要睡著之前,它怕自己睡過了頭,就會將前面位置上的牌子改為SIGNAL,因為每個人在離開隊列前都會回到自己座位上看一眼,如果看到牌子上狀態(tài)為SIGNAL,它就會去喚醒下一個人。只有保證前面位置上的牌子為SIGNAL,當(dāng)前線程才會安心的睡去。CONDITION狀態(tài)表示該線程在條件隊列中排隊,PROPAGATE狀態(tài)提醒后面來的線程可以直接獲取鎖,這個狀態(tài)只在共享模式用到,后面單獨講共享模式的時候會講到。

6. 結(jié)點進入同步隊列時會進行哪些操作?

//結(jié)點入隊操作, 返回前一個結(jié)點
private Node enq(final Node node) {
 for (;;) {
  //獲取同步隊列尾結(jié)點引用
  Node t = tail;
  //如果尾結(jié)點為空說明同步隊列還沒有初始化
  if (t == null) {
   //初始化同步隊列
   if (compareAndSetHead(new Node())) {
    tail = head;
   }
  } else {
   //1.指向當(dāng)前尾結(jié)點
   node.prev = t;
   //2.設(shè)置當(dāng)前結(jié)點為尾結(jié)點
   if (compareAndSetTail(t, node)) {
    //3.將舊的尾結(jié)點的后繼指向新的尾結(jié)點
    t.next = node;
    //for循環(huán)唯一的出口
    return t;
   }
  }
 }
}

注意,入隊操作使用一個死循環(huán),只有成功將結(jié)點添加到同步隊列尾部才會返回,返回結(jié)果是同步隊列原先的尾結(jié)點。下圖演示了整個操作過程。

讀者需要注意添加尾結(jié)點的順序,分為三步:指向尾結(jié)點,CAS更改尾結(jié)點,將舊尾結(jié)點的后繼指向當(dāng)前結(jié)點。在并發(fā)環(huán)境中這三步操作不一定能保證完成,所以在清空同步隊列所有已取消的結(jié)點這一操作中,為了尋找非取消狀態(tài)的結(jié)點,不是從前向后遍歷而是從后向前遍歷的。還有就是每個結(jié)點進入隊列中時它的等待狀態(tài)是為0,只有后繼結(jié)點的線程需要掛起時才會將前面結(jié)點的等待狀態(tài)改為SIGNAL。

注:以上全部分析基于JDK1.7,不同版本間會有差異,讀者需要注意。

以上就是本文的全部內(nèi)容,希望對大家的學(xué)習(xí)有所幫助,也希望大家多多支持腳本之家。

相關(guān)文章

  • spring?boot?Slf4j日志框架的體系結(jié)構(gòu)詳解

    spring?boot?Slf4j日志框架的體系結(jié)構(gòu)詳解

    在項目開發(fā)中記錄日志是必做的一件事情,springboot內(nèi)置了slf4j日志框架,下面這篇文章主要給大家介紹了關(guān)于spring?boot?Slf4j日志框架的體系結(jié)構(gòu),需要的朋友可以參考下
    2022-05-05
  • Spring Cloud Eureka 服務(wù)上下線監(jiān)控的實現(xiàn)

    Spring Cloud Eureka 服務(wù)上下線監(jiān)控的實現(xiàn)

    這篇文章主要介紹了Spring Cloud Eureka 服務(wù)上下線監(jiān)控的實現(xiàn),小編覺得挺不錯的,現(xiàn)在分享給大家,也給大家做個參考。一起跟隨小編過來看看吧
    2018-09-09
  • 使用springboot aop記錄接口請求的參數(shù)及響應(yīng)

    使用springboot aop記錄接口請求的參數(shù)及響應(yīng)

    該文章介紹了如何使用SpringAOP的切面注解,如@Before和@AfterReturning,來記錄Controller層的方法輸入?yún)?shù)和響應(yīng)結(jié)果,還討論了@Around注解的靈活性,允許在方法執(zhí)行前后進行更多控制,需要的朋友可以參考下
    2024-05-05
  • Java 實現(xiàn)簡單靜態(tài)資源Web服務(wù)器的示例

    Java 實現(xiàn)簡單靜態(tài)資源Web服務(wù)器的示例

    這篇文章主要介紹了Java 實現(xiàn)簡單靜態(tài)資源Web服務(wù)器的示例,幫助大家更好的理解和使用Java,感興趣的朋友可以了解下
    2020-11-11
  • 高級數(shù)據(jù)結(jié)構(gòu)及應(yīng)用之使用bitmap進行字符串去重的方法實例

    高級數(shù)據(jù)結(jié)構(gòu)及應(yīng)用之使用bitmap進行字符串去重的方法實例

    今天小編就為大家分享一篇關(guān)于高級數(shù)據(jù)結(jié)構(gòu)及應(yīng)用之使用bitmap進行字符串去重的方法實例,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧
    2019-02-02
  • springboot配置多數(shù)據(jù)源后mybatis攔截器失效的解決

    springboot配置多數(shù)據(jù)源后mybatis攔截器失效的解決

    這篇文章主要介紹了springboot配置多數(shù)據(jù)源后mybatis攔截器失效的解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2021-09-09
  • Java?SSM框架講解

    Java?SSM框架講解

    這篇文章主要介紹了什么是SSM框架,SSM框架是spring、spring?MVC?、和mybatis框架的整合,是標準的MVC模式。想進一步了解的同學(xué)可以詳細參考本文
    2023-03-03
  • Spring @value用法示例詳解

    Spring @value用法示例詳解

    這篇文章主要介紹了Spring-@value用法詳解,為了簡化讀取properties文件中的配置值,spring支持@value注解的方式來獲取,這種方式大大簡化了項目配置,提高業(yè)務(wù)中的靈活性,本文通過實例代碼給大家介紹的非常詳細,需要的朋友參考下吧
    2022-08-08
  • Java Robot應(yīng)用示例之機器人功能

    Java Robot應(yīng)用示例之機器人功能

    這篇文章主要為大家詳細介紹了Java Robot應(yīng)用示例之機器人功能,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-01-01
  • 帶著新人看java虛擬機01(推薦)

    帶著新人看java虛擬機01(推薦)

    這篇文章主要介紹了java虛擬機,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2019-04-04

最新評論