隊(duì)列先進(jìn)先出,棧先進(jìn)后出。
遍歷數(shù)據(jù)速度不同。
棧只能從頭部取數(shù)據(jù) 也就最先放入的需要遍歷整個(gè)棧最后才能取出來(lái),而且在遍歷數(shù)據(jù)的時(shí)候還得為數(shù)據(jù)開辟臨時(shí)空間,保持?jǐn)?shù)據(jù)在遍歷前的一致性;
隊(duì)列則不同,他基于地址指針進(jìn)行遍歷,而且可以從頭或尾部開始遍歷,但不能同時(shí)遍歷,無(wú)需開辟臨時(shí)空間,因?yàn)樵诒闅v的過程中不影像數(shù)據(jù)結(jié)構(gòu),速度要快的多。
Java8開始ConcurrentHashMap,為什么舍棄分段鎖?
ConcurrentHashMap的原理是引用了內(nèi)部的 Segment ( ReentrantLock ) 分段鎖,保證在操作不同段 map 的時(shí)候, 可以并發(fā)執(zhí)行, 操作同段 map 的時(shí)候,進(jìn)行鎖的競(jìng)爭(zhēng)和等待。從而達(dá)到線程安全, 且效率大于 synchronized。
但是在 Java 8 之后, JDK 卻棄用了這個(gè)策略,重新使用了 synchronized+CAS。
棄用原因:
通過 JDK 的源碼和官方文檔看來(lái), 他們認(rèn)為的棄用分段鎖的原因由以下幾點(diǎn):
加入多個(gè)分段鎖浪費(fèi)內(nèi)存空間;
生產(chǎn)環(huán)境中, map 在放入時(shí)競(jìng)爭(zhēng)同一個(gè)鎖的概率非常小,分段鎖反而會(huì)造成更新等操作的長(zhǎng)時(shí)間等待;
為了提高 GC 的效率;
提供了新的同步方案:既然棄用了分段鎖, 那么一定由新的線程安全方案, 我們來(lái)看看源碼是怎么解決線程安全的呢?(源碼保留了segment 代碼, 但并沒有使用)。
ConcurrentHashMap(JDK1.8)為什么要使用synchronized而不是如ReentranLock這樣的可重入鎖?
我想從下面幾個(gè)角度討論這個(gè)問題:
1. 鎖的粒度
首先鎖的粒度并沒有變粗,甚至變得更細(xì)了。每當(dāng)擴(kuò)容一次,ConcurrentHashMap的并發(fā)度就擴(kuò)大一倍。
2. Hash沖突
JDK1.7中,ConcurrentHashMap從過二次hash的方式(Segment -> HashEntry)能夠快速的找到查找的元素。在1.8中通過鏈表加紅黑樹的形式彌補(bǔ)了put、get時(shí)的性能差距。
JDK1.8中,在ConcurrentHashmap進(jìn)行擴(kuò)容時(shí),其他線程可以通過檢測(cè)數(shù)組中的節(jié)點(diǎn)決定是否對(duì)這條鏈表(紅黑樹)進(jìn)行擴(kuò)容,減小了擴(kuò)容的粒度,提高了擴(kuò)容的效率。
下面是我對(duì)那個(gè)面試問題的一些看法,即為什么是synchronized,而不是ReentranLock?
1. 減少內(nèi)存開銷
假設(shè)使用可重入鎖來(lái)獲得同步支持,那么每個(gè)節(jié)點(diǎn)都需要通過繼承AQS來(lái)獲得同步支持。但并不是每個(gè)節(jié)點(diǎn)都需要獲得同步支持的,只有鏈表的頭節(jié)點(diǎn)(紅黑樹的根節(jié)點(diǎn))需要同步,這無(wú)疑帶來(lái)了巨大內(nèi)存浪費(fèi)。
2. 獲得JVM的支持
可重入鎖畢竟是API這個(gè)級(jí)別的,后續(xù)的性能優(yōu)化空間很小。
synchronized則是JVM直接支持的,JVM能夠在運(yùn)行時(shí)作出相應(yīng)的優(yōu)化措施:鎖粗化、鎖消除、鎖自旋等等。這就使得synchronized能夠隨著JDK版本的升級(jí)而不改動(dòng)代碼的前提下獲得性能上的提升。
更多關(guān)于“Java培訓(xùn)”的問題,歡迎咨詢千鋒教育在線名師。千鋒已有十余年的培訓(xùn)經(jīng)驗(yàn),課程大綱更科學(xué)更專業(yè),有針對(duì)零基礎(chǔ)的就業(yè)班,有針對(duì)想提升技術(shù)的好程序員班,高品質(zhì)課程助力你實(shí)現(xiàn)java程序員夢(mèng)想。