golang-map是線程安全的嗎尤勋?

map

字典(map)它能存儲(chǔ)的不是單一值的集合喘落,而是鍵值對(duì)的集合茵宪。

什么是鍵值對(duì)?它是從英文 key-value pair 直譯過來的一個(gè)詞瘦棋。顧名思義稀火,一個(gè)鍵值對(duì)就代表
了一對(duì)鍵和值。一個(gè)“鍵”和一個(gè)“值”分別代表了一個(gè)從屬于某一類型的獨(dú)立值兽狭,把它們兩個(gè)捆綁在一
起就是一個(gè)鍵值對(duì)了憾股。在 Go 語言規(guī)范中鹿蜀,應(yīng)該是為了避免歧義箕慧,他們將鍵值對(duì)換了一種稱呼,
叫做:“鍵 - 元素對(duì)”茴恰。
Go 語言的字典類型其實(shí)是一個(gè)哈希表(hash table)的特定實(shí)現(xiàn)颠焦。在這個(gè)實(shí)現(xiàn)中,鍵和元素的
最大不同在于往枣, 的類型是受限的伐庭,而元素對(duì)卻可以是任意類型的。

代碼:

    ma := make(map[string]string)

    ma["key"] = "value"http:// 存儲(chǔ)

    value, ok := ma["key"]//獲取值分冈。ok:獲取狀態(tài)圾另,value:ok=true說明有值,否則反之
    fmt.Println(value, ok)
    
    //遍歷
    for key, value := range ma {
        fmt.Printf("key : %s, value:%s \n", key, value)
    }
    //刪除
    delete(ma, "key")
    

map:不是線程安全的雕沉。在同一時(shí)間段內(nèi)集乔,讓不同 goroutine 中的代碼,對(duì)同一個(gè)字典進(jìn)行讀寫操作是不安全
的坡椒。字典值本身可能會(huì)因這些操作而產(chǎn)生混亂扰路,相關(guān)的程序也可能會(huì)因此發(fā)生不可預(yù)知的問題。

sync.Map

在 2017 年發(fā)布的 Go 1.9 中正式加入了并發(fā)安全的字典類型sync.Map倔叼。這個(gè)字典類型提供了一些常用的鍵值存取操作方法汗唱,并保證了這些操作的并發(fā)安全。同時(shí)丈攒,它的存哩罪、取、刪等操作都可以基本保證在常數(shù)時(shí)間內(nèi)執(zhí)行完畢巡验。換句話說际插,它們的算法復(fù)雜度與map類型一樣都是O(1)的。在有些時(shí)候深碱,與單純使用原生map和互斥鎖的方案相比腹鹉,使用sync.Map可以顯著地減少鎖的爭(zhēng)用。sync.Map本身雖然也用到了鎖敷硅,但是功咒,它其實(shí)在盡可能地避免使用鎖愉阎。

代碼:

    var ma sync.Map// 該類型是開箱即用,只需要聲明既可
    ma.Store("key", "value") // 存儲(chǔ)值
    ma.Delete("key") //刪除值
    ma.LoadOrStore("key", "value")// 獲取值力奋,如果沒有則存儲(chǔ)
    fmt.Println(ma.Load("key"))//獲取值
    
    //遍歷
    ma.Range(func(key, value interface{}) bool {
        fmt.Printf("key:%s ,value:%s \n", key, value)
        //如果返回:false榜旦,則退出循環(huán),
        return true
    })

擴(kuò)展:

為什么map的鍵會(huì)有限制景殷?

Go 語言規(guī)范規(guī)定溅呢,在鍵類型的值之間必須可以施加操作符==和!=。換句話說猿挚,鍵類型的值必須要支持判等操作咐旧。由于函數(shù)類型、字典類型和切片類型的值并不支持判等操作绩蜻,所以字典的鍵

類型不能是這些類型铣墨。

map映射過程

哈希表中查找與某個(gè)鍵值對(duì)應(yīng)的那個(gè)元素值,那么我們需要先把鍵值作為參數(shù)傳給這個(gè)哈希表办绝。哈希表會(huì)先用哈希函數(shù)(hash function)把鍵值轉(zhuǎn)換為哈希值伊约。哈希值通常是一個(gè)無符號(hào)的整數(shù)。一個(gè)哈希表會(huì)持有一定數(shù)量的桶(bucket)孕蝉,也可稱之為哈希桶屡律,這些哈希桶會(huì)均勻地儲(chǔ)存其所屬哈希表收納的那些鍵 - 元素對(duì)。因此降淮,哈希表會(huì)先用這個(gè)鍵的哈希值的低幾位去定位到一個(gè)哈希桶超埋,然后再去這個(gè)哈希桶中,查找這個(gè)鍵骤肛。由于鍵 - 元素對(duì)總是被捆綁在一起存儲(chǔ)的纳本,所以一旦找到了鍵,就一定能找到對(duì)應(yīng)的元素值腋颠。隨后繁成,哈希表就會(huì)把相應(yīng)的元素值作為結(jié)果返回。只要這個(gè)鍵 - 元素對(duì)存在于哈希表中就一定會(huì)被查找到淑玫。

如何判斷那些類型作為字典的鍵比較合適?

在map查找的流程中巾腕,得知,“把鍵值轉(zhuǎn)換為哈希值”以及“把要查找的鍵值與哈希桶中的鍵值做對(duì)比”絮蒿, 是明顯兩個(gè)重要且比較耗時(shí)的操作尊搬。可以說:**求哈希和判等操作的速度越快土涝,對(duì)應(yīng)的類型就越適合作為鍵類型**佛寿。以求哈希的操作為例,寬度越小的類型速度通常越快。

基本類型:對(duì)于布爾類型冀泻、整數(shù)類型常侣、浮點(diǎn)數(shù)類型、復(fù)數(shù)類型和指針類型來說都是如此弹渔。對(duì)于字符串類型胳施,由于它的寬度是不定的,所以要看它的值的具體長度肢专,長度越短求哈希越快舞肆。類型的寬度是指它的單個(gè)值需要占用的字節(jié)數(shù)。比如博杖,bool椿胯、int8和uint8類型的一個(gè)值需要占用的字節(jié)數(shù)都是1,因此這些類型的寬度就都是1欧募。

高級(jí)類型压状。對(duì)數(shù)組類型的值求哈希實(shí)際上是依次求得它的每個(gè)元素的哈希值并進(jìn)行合并,所以速度就取決于它的元素類型以及它的長度

為什么說并發(fā)安全字典在盡量避免使用鎖跟继?

我們從它的代碼來解釋

//sync.Map 包的結(jié)構(gòu)
type Map struct {
   mu Mutex //鎖
   
   /*
        由read字段的類型可知,sync.Map在替換只讀字典的時(shí)候根本用不著鎖镣丑。另外舔糖,這個(gè)只讀字典
    在存儲(chǔ)鍵值對(duì)的時(shí)候,還在值之上封裝了一層莺匠。它先把值轉(zhuǎn)換為了unsafe.Pointer類型的值金吗,然后再把后者封     裝,并儲(chǔ)存在其中的原生字典中趣竣。如此一來摇庙,在變更某個(gè)鍵所對(duì)應(yīng)的值的時(shí)候,就也可以使用原子操作了遥缕。
   */
   read atomic.Value// 只讀字典
   /*
        它存儲(chǔ)鍵值對(duì)的方式與read字段中的原生字典一致卫袒,它的鍵類型也是interface{},并且同樣是把值先做   轉(zhuǎn)換和封裝后再進(jìn)行儲(chǔ)存的
   */
   dirty map[interface{}]*entry//臟字典单匣。
   misses int//重建的判斷條件
}

查找鍵值對(duì)的時(shí)候:會(huì)先去只讀字典中尋找夕凝,并不需要鎖定互斥鎖。只有當(dāng)確定“只讀字典中沒有户秤,但臟字典中可能會(huì)有這個(gè)鍵”的時(shí)候码秉,它才會(huì)在鎖的保護(hù)下去訪問臟字典。相對(duì)應(yīng)的鸡号,sync.Map在存儲(chǔ)鍵值對(duì)的時(shí)候转砖,只要只讀字典中已存有這個(gè)鍵,并且該鍵值對(duì)未被標(biāo)記為“已刪除”鲸伴,就會(huì)把新值存到里面并直接返回府蔗,這種情況下也不需要用到鎖莉兰。否則,它才會(huì)在鎖的保護(hù)下把鍵值對(duì)存儲(chǔ)到臟字典中礁竞。這個(gè)時(shí)候糖荒,該鍵值對(duì)的“已刪除”標(biāo)記會(huì)被
抹去。

當(dāng)一個(gè)鍵值對(duì)應(yīng)該被刪除模捂,但卻仍然存在于只讀字典中的時(shí)候捶朵,才會(huì)被用標(biāo)記為“已刪除”的方式進(jìn)行邏輯刪除,而不會(huì)直接被物理刪除狂男。

這種情況會(huì)在重建臟字典以后的一段時(shí)間內(nèi)出現(xiàn)综看。不過,過不了多久岖食,它們就會(huì)被真正刪除掉红碑。
在查找和遍歷鍵值對(duì)的時(shí)候,已被邏輯刪除的鍵值對(duì)永遠(yuǎn)會(huì)被無視泡垃。

對(duì)于刪除鍵值對(duì)析珊,sync.Map會(huì)先去檢查只讀字典中是否有對(duì)應(yīng)的鍵。如果沒有蔑穴,臟字典中可能有忠寻,那么它就會(huì)在鎖的保護(hù)下,試圖從臟字典中刪掉該鍵值對(duì)存和。最后奕剃,sync.Map會(huì)把該鍵值對(duì)中指向值的那個(gè)指針置為nil,這是另一種邏輯刪除的方式捐腿。

除此之外纵朋,還有一個(gè)細(xì)節(jié)需要注意,只讀字典和臟字典之間是會(huì)互相轉(zhuǎn)換的茄袖。在臟字典中查找鍵
值對(duì)次數(shù)足夠多的時(shí)候操软,sync.Map會(huì)把臟字典直接作為只讀字典,保存在它的read字段中绞佩,然
后把代表臟字典的dirty字段的值置為nil寺鸥。

在讀操作有很多但寫操作卻很少的情況下,并發(fā)安全字典的性能往往會(huì)更好品山。在幾個(gè)寫操作當(dāng)中胆建,新增鍵值對(duì)的操作對(duì)并發(fā)安全字典的性能影響是最大的,其次是刪除操作肘交,最后才是修改操作笆载。

如果被操作的鍵值對(duì)已經(jīng)存在于sync.Map的只讀字典中,并且沒有被邏輯刪除,那么修改它并
不會(huì)使用到鎖凉驻,對(duì)其性能的影響就會(huì)很小腻要。

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市涝登,隨后出現(xiàn)的幾起案子雄家,更是在濱河造成了極大的恐慌,老刑警劉巖胀滚,帶你破解...
    沈念sama閱讀 211,376評(píng)論 6 491
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件趟济,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡咽笼,警方通過查閱死者的電腦和手機(jī)顷编,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 90,126評(píng)論 2 385
  • 文/潘曉璐 我一進(jìn)店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來剑刑,“玉大人媳纬,你說我怎么就攤上這事∈┨停” “怎么了钮惠?”我有些...
    開封第一講書人閱讀 156,966評(píng)論 0 347
  • 文/不壞的土叔 我叫張陵,是天一觀的道長其监。 經(jīng)常有香客問我萌腿,道長,這世上最難降的妖魔是什么抖苦? 我笑而不...
    開封第一講書人閱讀 56,432評(píng)論 1 283
  • 正文 為了忘掉前任,我火速辦了婚禮米死,結(jié)果婚禮上锌历,老公的妹妹穿的比我還像新娘。我一直安慰自己峦筒,他們只是感情好究西,可當(dāng)我...
    茶點(diǎn)故事閱讀 65,519評(píng)論 6 385
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著物喷,像睡著了一般卤材。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上峦失,一...
    開封第一講書人閱讀 49,792評(píng)論 1 290
  • 那天扇丛,我揣著相機(jī)與錄音,去河邊找鬼尉辑。 笑死帆精,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播卓练,決...
    沈念sama閱讀 38,933評(píng)論 3 406
  • 文/蒼蘭香墨 我猛地睜開眼隘蝎,長吁一口氣:“原來是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來了襟企?” 一聲冷哼從身側(cè)響起嘱么,我...
    開封第一講書人閱讀 37,701評(píng)論 0 266
  • 序言:老撾萬榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎顽悼,沒想到半個(gè)月后曼振,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 44,143評(píng)論 1 303
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡表蝙,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 36,488評(píng)論 2 327
  • 正文 我和宋清朗相戀三年拴测,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片府蛇。...
    茶點(diǎn)故事閱讀 38,626評(píng)論 1 340
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡集索,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出汇跨,到底是詐尸還是另有隱情务荆,我是刑警寧澤,帶...
    沈念sama閱讀 34,292評(píng)論 4 329
  • 正文 年R本政府宣布穷遂,位于F島的核電站函匕,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏蚪黑。R本人自食惡果不足惜盅惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,896評(píng)論 3 313
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望忌穿。 院中可真熱鬧抒寂,春花似錦、人聲如沸掠剑。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,742評(píng)論 0 21
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽朴译。三九已至井佑,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間眠寿,已是汗流浹背躬翁。 一陣腳步聲響...
    開封第一講書人閱讀 31,977評(píng)論 1 265
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留澜公,地道東北人姆另。 一個(gè)月前我還...
    沈念sama閱讀 46,324評(píng)論 2 360
  • 正文 我出身青樓喇肋,卻偏偏與公主長得像,于是被迫代替她去往敵國和親迹辐。 傳聞我的和親對(duì)象是個(gè)殘疾皇子蝶防,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 43,494評(píng)論 2 348

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