天天看點

AQS2--出隊

 隊列不卡死,一定要:前面節點變成頭結點喚醒時候能夠喚醒後面節點,依次類推。

設定前面節點=-1就是為了前面節點走的時候,喚醒自己。

正常沒有阻塞節點,設定前面=-1,再旋轉一次嘗試擷取鎖,才阻塞。即使設定前面節點=-1之前,前面節點=0喚醒失敗走了,也不要緊,自己會再次旋轉一次擷取鎖。設定-1之後,前面節點=-1喚醒成功。自己旋轉擷取鎖失敗阻塞,前面節點=0喚醒失敗走了,是不可能的,因為阻塞前會設定前面=-1.

正常節點阻塞了,前面正常節點=0,那麼中間至少有一個異常節點,并且阻塞在異常節點上了。異常節點會去設定前面節點=-1。異常節點趕在前面節點喚醒之前設定-1就可以,趕在之後,那麼異常節點就要自己喚醒後面節點。

可以把異常節點作為前驅(錯了),就是不能把已經執行了if判斷的頭節點作為前驅,不然就卡死了。

一定要注意,前面節點變成head執行if (h != null && h.waitstatus != 0) 之前要設定為-1,否則就不能喚醒後面status正常節點(特例:除非後面節點自行出隊,還在旋轉)。

 是以正常節點要阻塞之前設定-1.

AQS2--出隊

上圖中,a沒有阻塞,head出隊執行if (h != null && h.waitstatus != 0)時候可以=0可以=-1,head=0出隊就a自己去擷取鎖,head=-1出隊喚醒a。是以隻要a沒有阻塞,就不需要前面節點出隊執行if判斷之前=-1。

 如果a阻塞了,就要異常的b去設定前面=-1,此時,一定要在異常節點b前面節點c出隊執行if (h != null && h.waitstatus != 0) 之前把前面c設定-1,如果前面節點出隊時候=0,a又阻塞了,a就永遠擷取不了鎖,隊列卡死。

但是,問題在于,正常節點和異常節點都無法知道前面節點是否已經出隊執行過了if (h != null && h.waitstatus != 0)。但是前面節點變成head時候thread=null,是以後面節點能夠知道的是前面節點是否是head。是以一定要在異常節點b前面節點c變成head之前把前面c設定-1(或者已經變成了head但是還沒有執行if(h.wautstatus!-0)的判斷),否則就喚醒a。

AQS2--出隊

 下面看異常節點設定前面節點=-1邏輯:

AQS2--出隊

 此時不知道head有沒有執行if (h != null && h.waitstatus != 0),如果沒有執行,此時設定head=-1,那麼head執行if (h != null && h.waitstatus != 0)就回去喚醒a,如果已經執行了,就不會喚醒任何節點,再去将head=-1,也沒用,是以就去喚醒異常節點的後面節點。

AQS2--出隊

 如果異常節點前面c!=head,但是c=1了(此時c可以=head),肯定不能建立後驅關系,c節點就一定可以保證a能夠喚醒,如果b再向前找沒必要。b來喚醒,我個人感覺是沒必要的,因為c可以一定喚醒a。

 如果異常節點前面c!=head,c沒有異常(此時c可以=head),但是c是頭節點了(c.thread = null),此時喚醒a。

注意:c變成head,再去unlock執行if (h != null && h.waitstatus != 0)中間有很多時間。是以c變成了head不一定執行了if(h.waitstatus!=0)的判斷。