synchronized map的value

作者: jerrychen26 (水澤)   2021-12-17 08:57:44
大家好,想請教各位前輩一個問題
今天我有一個map
Map<String, List<Integer>> map = new HashMap<>();
有一個function void safeAdd(String key, Integer value),
這個safeAdd方法要做的事情是 map.get(key).add(value) ,這邊先不用考慮list 是null
請問要如何做才能達到有效率而且是執行緒安全,以下是我的想法
1. 如果是synchronized safeAdd()這樣雖然安全,但是沒效率,因為就算不同key 也會要等別的key 完成才能進入
2. 同上,用ConcurrentHashMap一樣有這個問題
3. 在safeAdd 裡面 先 list A = map.get(key) ,再用synchronized (A) { A.add(value) } ,這是我覺得比較好的作法,但是IntelliJ給我警告說synchronization on local variable,但是get 出來的應該是物件的參考,這樣會有問題嗎
4. 同上,在方法裡面改用 synchronized (map.get(key)) {map.get(key).add(value)}
5. 不考慮用BlockingQueue 或 Vector,因為在safeAdd 裡面會有其他對list 的操作,例如用size() 去控制list的長度,我希望這個safeAdd被使用的時候同一時間只有單一執行緒對一個key 裡面的list 做操作。
我目前是使用4,我有測試過而且看起來是沒問題,但是還是怕有什麼意外因此上來發問,再麻煩各位幫我解答,謝謝。
作者: jej (晃奶大馬桶)   2021-12-17 09:55:00
你的Map放在全域變數 要thread safe就有困難如果不能放在block裡面 一定要放全域可以考慮用concurrent package下的lock或是使用 synchronized鎖定這個Map但如果你有效能考慮 還是建議你重構 看看能不能用singleton把map重構在block裡
作者: jerrychen26 (水澤)   2021-12-17 10:05:00
感謝回答,不過我的問題點比較在於當用synchronized(map.get(key)) 的block期間,可以保證被get出來的這個list 能達成執行緒安全嗎?
作者: jej (晃奶大馬桶)   2021-12-17 11:12:00
你這樣是鎖的東西是什麼就未知了 而且也不一定是單一物件多執行緒還是gg吧 所以才說要不要鎖map
作者: ssccg (23)   2021-12-17 13:00:00
ConcurrentHashMap.compute(key, (k, list) -> { list.add(value); return list });如果需要考慮list為空,就再加個檢查和new不過compute只會擋update類型的作業,你要達到類似DB交易(update中也block其他get)的話,就是get也改用compute想像你的需求大概是這樣 https://ideone.com/tCmTV2你的3 4作法其實效果一樣,IntelliJ的警告只是個提醒,真正的問題在於你synchonized list的期間,如果別的thread做了Map.put(key, ...),你的list是安全的,但是map.get(key)已不再是你的list而是別的東西,所以一樓才建議鎖map都用compute可以解決這問題更正,4的作法有個更糟的點是兩個map.get(key)間還有空窗,這中間map.put(key,...)的話,呼叫add的list跟上鎖的不同
作者: jerrychen26 (水澤)   2021-12-17 15:43:00
理解了,感謝解答
作者: darkk6 (Mr. Pan)   2021-12-22 01:37:00
做法跟你的 map/list 操作有關,看你是否預期他們會被改變會/不會,都有不同適合的寫法
作者: flowwinds (..)   2020-01-08 21:46:00
把key當mutex?

Links booklink

Contact Us: admin [ a t ] ucptt.com