ConcurrentHashMap和鎖
為什么HashMap數組的長度是2的指數次冪?
因為HashMap的底層是數組+鏈表+紅黑樹,在插入元素時,需要通過索引獲得插入元素的位置,計算索引的方法是使用哈希函數,將元素的哈希值與數組長度取模,當數組長度是2的指數次冪時,取模操作相當于對元素的哈希值進行二進制位與運算(假如數組長度是4,那么索引最大是3),可以更快的計算索引。

動態擴容時,原數組中的位置會移到什么位置?
每次擴容都是增加原來長度的倍數,原數組中的元素位置要么保持不動,要么等于舊數組時的索引位置再加上舊數組長度。
HsahMap在并發情況下為什么會造成死循環?
HashMap在并發執行put操作時發生擴容,如果一個線程被阻塞,另一個線程擴容結束后頭插法會使得鏈表順序與之前相反,而新數組在由阻塞的線程繼續進行擴容操作時會變成舊數組,在并發執行后就可能會出現節點丟失,產生環形鏈表等情況;jdk1.8中改為尾插法,避免了產生環形鏈表,但避免不了節點丟失。
JDK1.7 HashMap擴容使用頭插法
擴容后順序會改變
jdk1.7 ConcurrentHashMap
鎖
java中的鎖用來解決多線程并發情況下共享數據出現不一致的情況,對象頭中存儲鎖標志位

無鎖:沒有對共享資源進行鎖定(無競爭/樂觀鎖-CAS通過操作系統中一條指令來實現,能保證原子性)
偏向鎖:先確定鎖標志位,然后看mark word的前23bit記錄著偏向的線程ID,如果當前有多個線程在爭奪資源,則會變為輕量級鎖
輕量級鎖:線程在自己的虛擬機棧中開辟Lock Record的空間,線程通過CAS嘗試獲取鎖,一旦獲得將會使得對象頭中Mark Word的前30個bit指向Lock Rocrd,Lock Record中存放mark word的副本,并指向該對象。其它線程將會自旋等待,如果自旋等待的線程超過1個,會變成重量級鎖
重量級鎖:需要通過Monitor對線程進行管控
sychrinized: 底層使用Monitor 監視器
無鎖/樂觀鎖
CAS(compare and swap):new value代表想要將資源對象的狀態值更新之后的值,old value表示之前獲得的資源對象的值,若old value和當前資源對象的值不一樣,則放棄swap操作,然后一直自旋(不斷的重試CAS操作,配置循環次數防止死鎖)
CAS的原子性由CPU指令集/底層指令架構支持。
vloatile關鍵字:保證數據的可見性,JVM將該變量從線程本地內存強行刷新到主內存中
????????禁止指令重排,保證放在vloatile關鍵字之前的語句不會在后面執行,保證放在后面的語句不會提前執行。(多線程中發生指令重排,會影響結果)
AQS:同步框架
AQS成員變量
vloatile int state:使用int是因為資源有兩種模式分別為獨占模式和共享模式,當為共享模式時,可能會有多個線程在共享資源,所以state表示當前資源線程占用的數量,對其的修改使用CAS原子操作;
如果一個線程在當前沒有獲取到資源,那它可以進入隊列進行排隊,隊列是先進先出的雙向鏈表。

Node head、?Node tail
Node成員變量
線程對象、節點在隊列里的等待狀態、前后指針等,等待狀態有四個。
如何管理多線程同步狀態?
兩種使用場景:1、嘗試獲取鎖,沒有獲取到立即返回 ——tryAquire() 調用該方法,成功獲得鎖時對想用資源進行操作,操作完釋放,如果失敗還想繼續調用acquire()
??????? 2、獲取鎖,愿意進行隊列等待,直到獲取 acquire()
RenntrantLock,默認使用非公平鎖
非公平鎖:不按照鎖的順序分配,搶占式的,可能會有線程一直拿不到鎖(在很多情況下,非公平鎖的效率更高,當后請求的線程在前面休眠的線程恢復前拿到鎖,提高并發性)
公平鎖:鎖的分配按照請求鎖的順序分配,比如說AQS的隊列按照先進先出;如果當前線程已經獲取鎖,那么對state進行累加,可以繼續使用鎖,此外只有當前線程之前沒有其他線程的情況下才會嘗試獲取鎖,其它情況排隊。



浙公網安備 33010602011771號