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