關(guān)于容器類

在這里說一下茶敏,線程安全和線程不安全的容器類惊搏。請(qǐng)帶著質(zhì)疑的角度看待這篇文章恬惯,感恩。

前提


什么是線程安全浓恳?

通俗的說奖蔓,在多線程中吆鹤,在執(zhí)行一段代碼上洲守,仍然按照我們?cè)仍O(shè)想的邏輯運(yùn)行梗醇,產(chǎn)生我們意向到的結(jié)果叙谨。

學(xué)術(shù)的說,在一個(gè)線程調(diào)用共享資源的時(shí)候涤垫,什么時(shí)候讓其他線程蝠猬,或者怎么樣讓其他線程看到這個(gè)資源的變化榆芦。

java內(nèi)存模型如何應(yīng)對(duì)上述問題匆绣?

happend-before ,數(shù)據(jù)依賴性 旺入,as-if-serial ,? 內(nèi)存屏障,----這些都是禁止某些指令重排

happend-before 咐鹤,數(shù)據(jù)依賴性 圣絮,as-if-serial 這三個(gè)都是說扮匠,禁止某些重排序棒搜,按照前趨圖力麸,執(zhí)行。就是寫了才能讀闺鲸,開始了才能結(jié)束摸恍,有一個(gè)邏輯上的先后關(guān)系立镶。當(dāng)然happend-before有個(gè)更重要的一點(diǎn)

A hb B

B hb C

結(jié)果 : A hb C

內(nèi)存屏障:禁止某些重排序 + 緩存和主存之間數(shù)據(jù)的一致性問題谜慌。

如何保證線程安全莺奔,

鎖(synchronized / lock ) 变泄, CAS? 妨蛹, 寫時(shí)復(fù)制蛙卤,


正題


關(guān)于數(shù)據(jù)結(jié)構(gòu)颤难,分為邏輯上的和真實(shí)存儲(chǔ)在計(jì)算機(jī)中的數(shù)據(jù)的結(jié)構(gòu)行嗤。也可以分為真實(shí)的結(jié)構(gòu)(數(shù)組垛耳,鏈表)具體的程序員實(shí)現(xiàn)的抽象結(jié)構(gòu)(棧堂鲜,隊(duì)列 缔莲,堆酌予,圖抛虫,樹建椰,散列表).

java中常見的,我下面要說的數(shù)據(jù)結(jié)構(gòu):list , set , map

1.關(guān)于list :

有序(存儲(chǔ)的/插入的順序)屠列,意思就是排隊(duì)的內(nèi)個(gè)意思笛洛,每個(gè)人都拿著愛的號(hào)碼牌苛让,

可以重復(fù)

刪除狱杰,查找的時(shí)候要比較仿畸,數(shù)據(jù)類型為引用數(shù)據(jù)類型時(shí) 错沽, 類要重寫equals()方法。

單線程:

ArrayList和LinkedList的差別?

看過源碼都知道:ArrayList 用數(shù)組實(shí)現(xiàn),查詢效率高吴侦,插入刪除效率低备韧, LinkedList是用雙向鏈表實(shí)現(xiàn)的痪枫,插入刪除效率高 织堂,查詢效率低

這些單線程的都是強(qiáng)一致性奶陈,有一個(gè)字段modCount (對(duì)象被修改次數(shù))這個(gè)字段作用:在用迭代器的遍歷中易阳,這個(gè)時(shí)候其他線程修改了數(shù)據(jù)的結(jié)構(gòu)(增/刪),就會(huì)報(bào)ConcurrentModificationException異常吃粒。

多線程安全:

1.Vector.Class? 潦俺, 在每個(gè)方法上加synchronized關(guān)鍵字,加鎖實(shí)現(xiàn)線程安全徐勃。

2.Collections.synchronizedList(....) ,也是通過加synchronized鎖來實(shí)現(xiàn)線程安全

3.juc包下CopyOnWriteArrayList.Class 數(shù)組實(shí)現(xiàn)僻肖,適用在讀多肖爵,寫少的場(chǎng)合中。

- 使用 ReentrantLockvolatile? 和 寫時(shí)復(fù)制 臀脏,三個(gè)方面解決線程安全問題

- volatile : 解決數(shù)組的內(nèi)存可見性

-RentrantLock 劝堪,鎖住的是:修改容器內(nèi)的數(shù)據(jù)的時(shí)候的只允許一個(gè)線程修改冀自。

-寫時(shí)復(fù)制:保證如果有線程正在修改容器內(nèi)的數(shù)據(jù),這個(gè)時(shí)候有線程想讀幅聘,讀取容器內(nèi)的數(shù)據(jù)凡纳,修改的是復(fù)制的版本,修改之后把數(shù)據(jù)在刷新在容器內(nèi)帝蒿,因?yàn)檫@個(gè)容器內(nèi)的數(shù)據(jù)是volatile荐糜, 保證多線程中的內(nèi)存可見性。



2.關(guān)于Map

這里先說Map葛超,因?yàn)榇蟛糠諷et中數(shù)據(jù)的存儲(chǔ)在對(duì)應(yīng)Map中的Key中暴氏。

容器內(nèi)的數(shù)據(jù)類型,如果是引用數(shù)據(jù)類型绣张,要重寫hashCode()方法和equals()方法答渔,因?yàn)樘砑訉?duì)象步驟如下;

首先調(diào)用hashCode()方法,(此計(jì)算的對(duì)象哈希值決定了Set中的存儲(chǔ)位置):若此位置之前沒有對(duì)象存儲(chǔ)侥涵,則這個(gè)對(duì)象沼撕,直接存儲(chǔ)到此位置上;若有存儲(chǔ)對(duì)象,通過equals方法芜飘,比較這兩個(gè)對(duì)象是否相同务豺,如果相同,后一個(gè)對(duì)象就不能再添加嗦明,萬一返回false呢笼沥,則都存儲(chǔ)在"同一個(gè)位置"。


單線程:

1.HashMap (數(shù)組 + 鏈表+紅黑樹 娶牌,null ok)

問題:HashMap? get() / put()的工作原理奔浅?

存儲(chǔ)過程:

0.hash(key.hashCode())? 充分使用key的hashCode值的前16位和后16位 “&”操作 得到一個(gè) int? hash值

1.判斷table是否為null,是就擴(kuò)容

2.這個(gè)hash值 & (數(shù)組Size -1 ) , 得出找到要放在桶上的索引,如果這個(gè)位置上沒值诗良,就保存key -value汹桦,

3.如果索引位置上有值,再equalse()判斷是否相等累榜,如果相等营勤,就覆蓋old value

4. 如果索引位置上的值和插入的值不相等,就檢查這個(gè)節(jié)點(diǎn)是不是treeNode壹罚,如果是就紅黑樹插入key-value 葛作,這里依然要equals(),如果相等就 覆蓋 old value

5. 如果不是紅黑樹猖凛,那么遍歷他的鏈表赂蠢,遍歷的過程中做兩件事? 1.檢查是否遍歷到了鏈表的尾端,如果是就插入到尾端檢查這個(gè)時(shí)候鏈表的長(zhǎng)度是否是8辨泳,是的話 虱岂,(判斷數(shù)組的length如果大于64玖院,就把鏈表轉(zhuǎn)化為紅黑樹,小于64就擴(kuò)容)? ? 2.equals()于當(dāng)前鏈上結(jié)點(diǎn)是否相等第岖,相等就跳出遍歷难菌,覆蓋old valueB

6.覆蓋了old value會(huì)返回old value , 沒有覆蓋就modCount +1 ,檢查是否需要擴(kuò)容

get() 過程:

1. hash(key.hashCode()) & (table.length -1) 找到索引蔑滓,桶的位置

2.索引上沒有值郊酒,返回null , 桶上有值equals() 相等,返回;

3.不等就看這個(gè)節(jié)點(diǎn)是不是treeNode键袱,是就查找equals()燎窘,true返回值,false 就返回null

4.不是treeNode 蹄咖, 就查看這個(gè)有沒有next個(gè)節(jié)點(diǎn)鏈褐健,遍歷,equals()澜汤,同上蚜迅。

問題:HashMap中解決碰撞的方法?

首先知道幾個(gè)數(shù)字 :

16 : capacity 默認(rèn)數(shù)組的長(zhǎng)度 , 不默認(rèn)就2的冪次方俊抵,減少碰撞慢叨。

8:當(dāng)值插入值后鏈表長(zhǎng)度變成 8 就把鏈表變?yōu)榧t黑樹,或者擴(kuò)容

擴(kuò)容相關(guān)參數(shù)

0.75:final? loadFactor 負(fù)載因子,當(dāng)添加key-value之后 务蝠, 檢查當(dāng)前的容量 size 是否達(dá)到了threshold(capacity*loadFactor)閾值,達(dá)到了就擴(kuò)容烛缔。至于為什么是0.75呢馏段?javadoc上說,0.75滿足泊松分布践瓷。

64: final院喜,最小轉(zhuǎn)變?yōu)榧t黑樹的這個(gè)一個(gè)閾值: 在鏈表轉(zhuǎn)紅黑樹中 , 如果map中table 的長(zhǎng)度沒有達(dá)到這個(gè)值晕翠,就會(huì)通過擴(kuò)容解決沖突喷舀,并不會(huì)鏈表轉(zhuǎn)紅黑樹

總結(jié):這個(gè)如何解決沖突,就是在桶后面加了一個(gè)鏈表淋肾,為了解決鏈表過長(zhǎng)查找效率變?yōu)镺(n),鏈表過長(zhǎng)變?yōu)榧t黑樹查找效率變?yōu)镺(logN)

問題:HashMap的擴(kuò)容硫麻?

擴(kuò)容就是變成原來table.length的兩倍,這樣的好處樊卓,關(guān)于整體結(jié)構(gòu)上的動(dòng)蕩拿愧,這個(gè)值,要不就在 原來的索引上碌尔,要不就在原來的索引+原來table.length處浇辜。多線程擴(kuò)容會(huì)產(chǎn)生循環(huán)鏈表券敌,引起死鎖,讓Cpu的使用率變成100%

繼承HashMap的LinkedHashMap:

就是在HashMap的基礎(chǔ)上柳洋,加了雙向鏈表待诅,維護(hù)了一個(gè)順序,默認(rèn)是插入的順序熊镣,可以用過構(gòu)造方法指定accessOrder為true, 然后重寫LinkedHashMap中的removeEldestEntry方法? 這樣維護(hù)的是LRU(最近最久未使用淘汰)順序卑雁。

問題:如何維護(hù)順序問題?

LinkedHashMap 繼承HashMap之后重寫了三個(gè)鉤子方法轧钓,用著三個(gè)方法來維護(hù)一個(gè)順序問題

2.TreeMap(紅黑樹)

自定義key的排序? 序厉,在數(shù)量級(jí)很大的情況下使用TreeMap。關(guān)于key為null的問題 毕箍, 可以put存儲(chǔ),如果get讀的時(shí)候回報(bào)錯(cuò)NullPointerException異常弛房,但是可以遍歷,迭代器讀取

多線程安全:

1.Hashtable : (不允許key為 null)加synchronized 關(guān)鍵字實(shí)現(xiàn)同步方法 .

2.Collections.synchronizedMap() /synchronizedSortedMap()

3.ConcurrentHashMap ( 數(shù)據(jù)類型和HashMap一樣而柑,不能為null ) :?

volatile table? 文捶, CAS方法一些狀態(tài)值的維護(hù)或者插入? , synchronized(node)媒咳,三個(gè)原子操作

問題:put() / get()的工作原理粹排?HashMap有什么不一樣呢 ?

put()

0.這里寫了循環(huán)涩澡,只有插入成功才會(huì)返回顽耳。

1.判斷當(dāng)桶上是不是有值 ,CAS插入值妙同,插入成功就返回射富,有值之后先判斷是否有其他線程是否在擴(kuò)容,是就幫助擴(kuò)容粥帚,沒有擴(kuò)容就接著插入胰耗。

2.加鎖的粒度不是整個(gè)這個(gè)方法,加鎖是在node結(jié)點(diǎn)上芒涡,這個(gè)時(shí)候synchronized(當(dāng)前這個(gè)桶)柴灯,下面和HashMap一樣,是鏈表就插在鏈表后面(這個(gè)之后要判斷鏈表長(zhǎng)度是否大于8费尽,當(dāng)前table.length是否大于64赠群,是就轉(zhuǎn)變?yōu)榧t黑樹),是紅黑樹就按照紅黑樹插入依啰。

3.插入之后判斷是否要擴(kuò)容

問題:關(guān)于擴(kuò)容的問題乎串?很難

在ConcurrentHashMap里,sizeCtl承擔(dān)map擴(kuò)容的問題,并增加了一點(diǎn)功能叹誉。

當(dāng)sizeCtl=-1鸯两,代表正在初始化。

當(dāng)sizeCtl=-N长豁,代表有N-1個(gè)線程在擴(kuò)容钧唐。

當(dāng)sizeCtl=0,代表需要初始化匠襟。

以上三個(gè)值钝侠,都是狀態(tài)位。

當(dāng)sizeCtl>0酸舍,即擴(kuò)容的閾值帅韧,也即總?cè)萘?* 0.75。

多線程擴(kuò)容啃勉,每個(gè)線程都擴(kuò)容自己分到的桶忽舟,擴(kuò)容完成后把node結(jié)點(diǎn)變?yōu)镕orwardingNode,通知其他線程這個(gè)桶已經(jīng)擴(kuò)容完畢淮阐,并且設(shè)置桶上hash值為 MOVED

問題:ConcurrentHashMap的弱一致性問題叮阅?

如果有線程遍歷,這個(gè)時(shí)候有線程做修改數(shù)據(jù)結(jié)構(gòu)的事情泣特?

在使用迭代器時(shí)浩姥,如果修改的部分發(fā)生在還沒有迭代的地方,就會(huì)顯示出來状您,如果迭代在已經(jīng)迭代的地方就不會(huì)顯示出來

問題:ConcurrentHashMap并行度勒叠?

讀完全并行。

寫部分并行膏孟,因?yàn)閟ynchronized的是桶上的node , 所以table的length有多少缴饭,并行度就是多少。

迭代器

3.ConcurrentSkipListMap :

ConcurrentHashMap是HashMap的 多線程版本骆莹,ConcurrentSkipListMap是TreeMap的多線程版本,對(duì)key有一個(gè)排序担猛。

無阻塞幕垦,所有的操作都是并行的 , 跳表基于鏈表 傅联, 這個(gè)結(jié)構(gòu)查找類似二分查找先改。這個(gè)以后再寫



3.關(guān)于Set

1.無序(存儲(chǔ)位置 根據(jù)Hash值),他和list一樣都繼承Collection.Class

2.不可重復(fù)

單線程:

HashSet , TreeSet 這些類各自實(shí)現(xiàn) HashMap 蒸走, TreeMap仇奶,以其中的key保存自己的值,value是一個(gè)默認(rèn)的值比驻。

線程安全:

1.Collections.synchronizedSet

2.CopyOnWriteArraySet()

同CopyOnWriteArrayList一樣, 使用volatile 數(shù)組 + lock +寫時(shí)復(fù)制 该溯,實(shí)現(xiàn)線程安全岛抄。

3.ConcurrentSkipListSet ; 基于ConcurrentSkipListMap實(shí)現(xiàn)的

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市狈茉,隨后出現(xiàn)的幾起案子夫椭,更是在濱河造成了極大的恐慌,老刑警劉巖氯庆,帶你破解...
    沈念sama閱讀 217,542評(píng)論 6 504
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蹭秋,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡堤撵,警方通過查閱死者的電腦和手機(jī)仁讨,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,822評(píng)論 3 394
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來实昨,“玉大人洞豁,你說我怎么就攤上這事⊥篱希” “怎么了族跛?”我有些...
    開封第一講書人閱讀 163,912評(píng)論 0 354
  • 文/不壞的土叔 我叫張陵,是天一觀的道長(zhǎng)锐墙。 經(jīng)常有香客問我礁哄,道長(zhǎng),這世上最難降的妖魔是什么溪北? 我笑而不...
    開封第一講書人閱讀 58,449評(píng)論 1 293
  • 正文 為了忘掉前任桐绒,我火速辦了婚禮,結(jié)果婚禮上之拨,老公的妹妹穿的比我還像新娘茉继。我一直安慰自己,他們只是感情好蚀乔,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,500評(píng)論 6 392
  • 文/花漫 我一把揭開白布烁竭。 她就那樣靜靜地躺著,像睡著了一般吉挣。 火紅的嫁衣襯著肌膚如雪派撕。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,370評(píng)論 1 302
  • 那天睬魂,我揣著相機(jī)與錄音终吼,去河邊找鬼。 笑死氯哮,一個(gè)胖子當(dāng)著我的面吹牛际跪,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 40,193評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼姆打,長(zhǎng)吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼良姆!你這毒婦竟也來了?” 一聲冷哼從身側(cè)響起穴肘,我...
    開封第一講書人閱讀 39,074評(píng)論 0 276
  • 序言:老撾萬榮一對(duì)情侶失蹤歇盼,失蹤者是張志新(化名)和其女友劉穎,沒想到半個(gè)月后评抚,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體豹缀,經(jīng)...
    沈念sama閱讀 45,505評(píng)論 1 314
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,722評(píng)論 3 335
  • 正文 我和宋清朗相戀三年慨代,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了邢笙。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片。...
    茶點(diǎn)故事閱讀 39,841評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡侍匙,死狀恐怖氮惯,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情想暗,我是刑警寧澤妇汗,帶...
    沈念sama閱讀 35,569評(píng)論 5 345
  • 正文 年R本政府宣布,位于F島的核電站说莫,受9級(jí)特大地震影響杨箭,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜储狭,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,168評(píng)論 3 328
  • 文/蒙蒙 一互婿、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧辽狈,春花似錦慈参、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,783評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至着茸,卻和暖如春僧凤,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背元扔。 一陣腳步聲響...
    開封第一講書人閱讀 32,918評(píng)論 1 269
  • 我被黑心中介騙來泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留旋膳,地道東北人澎语。 一個(gè)月前我還...
    沈念sama閱讀 47,962評(píng)論 2 370
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親擅羞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子尸变,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,781評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 前言 這次我和大家一起學(xué)習(xí)HashMap,HashMap我們?cè)诠ぷ髦薪?jīng)常會(huì)使用减俏,而且面試中也很頻繁會(huì)問到召烂,因?yàn)樗?..
    liangzzz閱讀 7,983評(píng)論 7 102
  • HashMap 是 Java 面試必考的知識(shí)點(diǎn),面試官?gòu)倪@個(gè)小知識(shí)點(diǎn)就可以了解我們對(duì) Java 基礎(chǔ)的掌握程度娃承。網(wǎng)...
    野狗子嗷嗷嗷閱讀 6,667評(píng)論 9 107
  • 摘要 HashMap是Java程序員使用頻率最高的用于映射(鍵值對(duì))處理的數(shù)據(jù)類型奏夫。隨著JDK(Java Deve...
    周二倩你一生閱讀 1,250評(píng)論 0 5
  • 清華備考生閱讀 89評(píng)論 0 0
  • 近的,遠(yuǎn)了 遠(yuǎn)了历筝,近著 清晰的酗昼,模糊了 模糊的,清晰著 青春梳猪,走了 新生麻削,來了 熟悉的,陌生了 陌生了春弥,溫暖著 愛...
    馮詩遠(yuǎn)閱讀 125評(píng)論 0 0