synchronized是JVM層面實現(xiàn)的鎖,而AQS是JDK層面實現(xiàn)的鎖。
關(guān)于synchronized鎖,可以看之前寫的這篇:
synchronized鎖關(guān)鍵字的膨脹性與用法
其實AQS和synchronized在實現(xiàn)鎖的原理上是一樣的,只是AQS是借助了同步隊列去進行自旋和阻塞,利用條件隊列去實現(xiàn)Object的對象方法,去完成等待和喚醒。
我們要注意的是同步隊列是等待獲取鎖的隊列,條件隊列是曾經(jīng)獲取到鎖,但是因為類似(滿隊入隊/空隊出隊)這樣的阻塞行為,而避免一直阻塞,進行調(diào)節(jié)使用的。
廢話不多說,我們從一個實際的場景出發(fā)去講一下這個原理。
1、線程A,B,C想要搶占一個資源做操作;
2、線程A先得頭籌獲得了對象的鎖。在它持有資源的同時,其他線程就會被阻塞,依次加入到同步隊列中去,順序為B,C;
3、每一個Node入隊都會自旋檢查自己前一個節(jié)點是不是signal狀態(tài),如果是signal狀態(tài),就阻塞自己,等待喚醒;如果不是,就自旋【嘗試獲取鎖,獲取不到就嘗試將它變成signal狀態(tài)】這個過程;
4、當線程A執(zhí)行完畢后,它會釋放鎖,然后喚醒同步隊列的頭結(jié)點,這時候頭結(jié)點B就會去獲取鎖;
5、當B獲得鎖之后,他發(fā)現(xiàn)之前線程A的操作讓當前資源隊列的元素為空,而它要做的操作是一個take()操作。無法執(zhí)行,會一直阻塞。要怎么辦呢?
6、這時候就會把它添加到條件隊列當中,然后釋放鎖,讓其他線程獲取資源,直到滿足線程B執(zhí)行操作的條件為止;
7、釋放鎖之后,喚醒了同步隊列的頭結(jié)點C,此時C獲得了資源。它對著資源隊列執(zhí)行了put()操作;
8、此時,條件隊列中的節(jié)點B將被喚醒,從條件隊列轉(zhuǎn)移到同步隊列的隊尾中去;
9、等線程C執(zhí)行完put()操作,釋放鎖,然后喚醒了同步隊列中的第一個節(jié)點B;
10、然后線程B執(zhí)行take()操作。并發(fā)結(jié)束。
以上就是一個簡單的鎖模型。
對于鎖的狀態(tài)state,是一個int值。
還有一個超時時間。這些是子類自己去實現(xiàn)規(guī)則。
同步隊列和條件隊列的元素都是Node
Node中主要有這些參數(shù):
waitStatus 當前節(jié)點的狀態(tài)。主要有0(初始化),1(取消),-1(SINGAL)【同步隊列】,-2(CONDITION)【條件隊列】,-3(PROPAGATE )【共享鎖】
是排他鎖還是共享鎖是根據(jù)Node中的字段標記的。
nextWaiter,在條件隊列中,指下一個節(jié)點;在同步隊列中,指什么屬性的鎖。
Thread 綁定的當前線程。