Java : 方法接受一个 Map 参数,需要对这个 map 做遍历。怎样才是安全的?

2018-09-28 16:21:30 +08:00
 theworldsong

举例:

test(Map<String, String> m) {

}

现在要在 test 内遍历 m。

但是:

老铁们,有办法吗

6403 次点击
所在节点    程序员
42 条回复
reeco
2018-09-28 23:40:46 +08:00
外部 remove 了跟你内部怎么遍历没有冲突啊,至于数据同步的问题就是楼上说的谁调用谁负责
miao1007
2018-09-28 23:42:02 +08:00
最开始应该上写时复制
lovedebug
2018-09-28 23:46:16 +08:00
所有调用 test 函数的地方强制使用 Collections.unmodifiedXX
Ginsai
2018-09-29 00:54:07 +08:00
既然只是传入 Map,外部能随时修改的话对 test()方法操作本身就已经不安全了。。如果没法将传入 Map 的类型线程安全的,那么就在 test()里面做 try catch,设置遍历失败尝试次数,异常捕获之后继续遍历处理。
laxenade
2018-09-29 04:01:51 +08:00
就和 spinlock 的原理一样不断拷贝咯,直到没有异常为止。
fengdianxun
2018-09-29 06:59:05 +08:00
用 kotlin 的只读 map 呢
hearfish
2018-09-29 07:02:29 +08:00
并发的环境下多线程共用一个不知道是不是线程安全的 Map,而且还不知道别的线程是怎么用它的?
MoHen9
2018-09-29 07:53:33 +08:00
把 map 改为 ConcurrentHashMap 呢,如果不允许,那么在传过来的时候就应该是一个拷贝的 map,而不是原始的 map
mifly
2018-09-29 08:05:41 +08:00
这样的代码应该要避免,考虑看看是不是可以用 blockquene 替代 map
assiadamo
2018-09-29 08:38:25 +08:00
上面的 boywang 貌似对不可变集合有不同的观点,我为了避免遍历中对原始集合有增改的这个问题,一般都不会去操作原始集合,而是直接做一个新的集合然后赋值,直接切换引用,这个是绝对线程安全的,而且遍历时切换也对遍历毫无影响
D3EP
2018-09-29 08:42:37 +08:00
@assiadamo 你这说的不就是 copyonwrite 的集合么?都是现成的线程安全的集合
D3EP
2018-09-29 08:43:33 +08:00
多线程条件下,不用线程安全的集合,我也是醉了
ilaipi
2018-09-29 08:50:35 +08:00
感觉你这既然外部可以随时修改这个 map,是不是这个 map 是个常量?那是不是不需要传进来?统一的地方去维护这个 map 然后上锁?
assiadamo
2018-09-29 09:05:08 +08:00
@D3EP copyonwrite 帮你省掉了切换引用这步操作,让你可以直接 add remove,但需要原始集合声明为 copyonwrite
TommyLemon
2018-09-29 09:42:03 +08:00
序列化再反序列化就行了。
可以用 fastjson( https://github.com/topics/fastjson)
ColinWang
2018-09-29 10:06:46 +08:00
Copy-On-Write 正解
ZSeptember
2018-09-29 11:25:14 +08:00
设计有问题,在多个地方都会写的不应该传参。
改用共享的,然后用线程安全的。
lihongjie0209
2018-09-29 11:51:14 +08:00
你这个接口有问题, 一个接口连自己的参数都无法处于一个确定的状态, 那你怎么写业务逻辑?
passerbytiny
2018-09-29 15:00:34 +08:00
引用以下前面的回复

#19 只是在 test 遍历,没有新启动一个新线程进行遍历的话,应该无须考虑同步问题,谁调用谁负责(方法调用者在自己的线程去考虑和别的线程同步)

# 37 你这个接口有问题, 一个接口连自己的参数都无法处于一个确定的状态, 那你怎么写业务逻辑?

如果你这个方法的参数类型是 java.util.Map ,又没有对外部调用做任何附加规范限制,那就意味着该方法声明了它处理的是不安全的 Map,出错是正常的,需要外部系统确认自己处理 Map 的线程安全问题。如果你需要这个方法自己考虑同步问题,那么它至少需要在规范上限制传入的参数类型是线程同步的或者不可变的。

从你的描述看,调用方非常明确的告诉你传入的是一个没有线程同步还被多线程共享的 java.util.Map 对象。你赶紧把这个 BUG 提交上去吧,技术层面解决不了。

解释一下:只有多线程共享、并且线程不安全的 Map 对象,才有可能出现这边正在处理着,那边 put/remove 的情况。
luozic
2018-09-29 19:59:08 +08:00
这种多线程请求还不同步的,脑袋被踢了? 暂时可以抽象为,只有一个线程有取数据和移除数据的操作,其他线程都从提交到这个线程处理。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/493552

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX