test(Map<String, String> m) {
}
现在要在 test 内遍历 m。
1
af463419014 2018-09-28 16:26:18 +08:00
入参 m 的类型改成 ImmutableMap
|
2
yinzhili 2018-09-28 16:32:56 +08:00 2
com.google.common.collect.ImmutableMap
|
3
xuhaoyangx 2018-09-28 16:46:47 +08:00
你这问题 让我想起了 Java 形参和实参
删除操作用 Iterator 去做 |
4
z3jjlzt 2018-09-28 16:49:52 +08:00
final 修饰入参
|
5
johnj 2018-09-28 17:34:15 +08:00
Collections.unmodifiableMap() 包一下
|
6
johnj 2018-09-28 17:35:33 +08:00
我说的不对
|
8
linlinismine 2018-09-28 18:28:57 +08:00
copy 一份
|
9
psuwgipgf 2018-09-28 18:37:52 +08:00
不明白怎么解决,关注一下。
|
10
aa6563679 2018-09-28 18:43:28 +08:00 via iPhone
@linlinismine 可能 copy 中被改了
|
11
crayygy 2018-09-28 18:45:05 +08:00 via Android
8l 的方法是可以的,在传入之前先从原 collection 中复制一份,然后再传入复制出来的对象
|
12
elgae 2018-09-28 18:55:59 +08:00
加个锁,遍历时独占
|
13
sagaxu 2018-09-28 19:22:10 +08:00 via Android 1
Map 只是个接口,你需要一个支持并发读写的实现
|
14
talen666 2018-09-28 19:36:28 +08:00
把声明 Map 的地方改成线程安全的 Map
|
16
oaix 2018-09-28 20:54:40 +08:00
重试
|
17
boywang004 2018-09-28 21:45:42 +08:00
只要调用 iterator()都会有机会上抛 ConcurrentModifyException,先做保护性拷贝,拷贝期间肯定会调用 iterator(),只要捕获 CME 重新拷贝,直到某次拷贝时没有改动成功拷出来为止……
上面说的加锁包 unmodifiableMap 啥的……=__=b |
18
micean 2018-09-28 22:38:38 +08:00
只能在 test 里面操作的话根本做不到
|
19
ysweics 2018-09-28 22:45:56 +08:00
明确一下问题,具体的需求是怎么样,比如这个 map 开始传递过来的时候只有 10 个 kv ,然后你在便利的时候,remove 两个 kv,你需要的结果是开始的 10 个 kv,还是剩余的 8 个 kv
|
20
zzorzz 2018-09-28 23:24:51 +08:00 1
只是在 test 遍历,没有新启动一个新线程进行遍历的话,应该无须考虑同步问题,谁调用谁负责(方法调用者在自己的线程去考虑和别的线程同步)
|
21
reeco 2018-09-28 23:40:46 +08:00
外部 remove 了跟你内部怎么遍历没有冲突啊,至于数据同步的问题就是楼上说的谁调用谁负责
|
22
miao1007 2018-09-28 23:42:02 +08:00 via Android
最开始应该上写时复制
|
23
lovedebug 2018-09-28 23:46:16 +08:00
所有调用 test 函数的地方强制使用 Collections.unmodifiedXX
|
24
Ginsai 2018-09-29 00:54:07 +08:00
既然只是传入 Map,外部能随时修改的话对 test()方法操作本身就已经不安全了。。如果没法将传入 Map 的类型线程安全的,那么就在 test()里面做 try catch,设置遍历失败尝试次数,异常捕获之后继续遍历处理。
|
25
laxenade 2018-09-29 04:01:51 +08:00
就和 spinlock 的原理一样不断拷贝咯,直到没有异常为止。
|
26
fengdianxun 2018-09-29 06:59:05 +08:00 via Android
用 kotlin 的只读 map 呢
|
27
hearfish 2018-09-29 07:02:29 +08:00
并发的环境下多线程共用一个不知道是不是线程安全的 Map,而且还不知道别的线程是怎么用它的?
|
28
MoHen9 2018-09-29 07:53:33 +08:00 via Android
把 map 改为 ConcurrentHashMap 呢,如果不允许,那么在传过来的时候就应该是一个拷贝的 map,而不是原始的 map
|
29
mifly 2018-09-29 08:05:41 +08:00 via Android
这样的代码应该要避免,考虑看看是不是可以用 blockquene 替代 map
|
30
assiadamo 2018-09-29 08:38:25 +08:00 via Android
上面的 boywang 貌似对不可变集合有不同的观点,我为了避免遍历中对原始集合有增改的这个问题,一般都不会去操作原始集合,而是直接做一个新的集合然后赋值,直接切换引用,这个是绝对线程安全的,而且遍历时切换也对遍历毫无影响
|
32
D3EP 2018-09-29 08:43:33 +08:00 via iPhone
多线程条件下,不用线程安全的集合,我也是醉了
|
33
ilaipi 2018-09-29 08:50:35 +08:00
感觉你这既然外部可以随时修改这个 map,是不是这个 map 是个常量?那是不是不需要传进来?统一的地方去维护这个 map 然后上锁?
|
34
assiadamo 2018-09-29 09:05:08 +08:00 via Android
@D3EP copyonwrite 帮你省掉了切换引用这步操作,让你可以直接 add remove,但需要原始集合声明为 copyonwrite
|
35
TommyLemon 2018-09-29 09:42:03 +08:00
序列化再反序列化就行了。
可以用 fastjson( https://github.com/topics/fastjson) |
36
ColinWang 2018-09-29 10:06:46 +08:00
Copy-On-Write 正解
|
37
ZSeptember 2018-09-29 11:25:14 +08:00
设计有问题,在多个地方都会写的不应该传参。
改用共享的,然后用线程安全的。 |
38
lihongjie0209 2018-09-29 11:51:14 +08:00 1
你这个接口有问题, 一个接口连自己的参数都无法处于一个确定的状态, 那你怎么写业务逻辑?
|
39
passerbytiny 2018-09-29 15:00:34 +08:00
引用以下前面的回复
#19 只是在 test 遍历,没有新启动一个新线程进行遍历的话,应该无须考虑同步问题,谁调用谁负责(方法调用者在自己的线程去考虑和别的线程同步) # 37 你这个接口有问题, 一个接口连自己的参数都无法处于一个确定的状态, 那你怎么写业务逻辑? 如果你这个方法的参数类型是 java.util.Map ,又没有对外部调用做任何附加规范限制,那就意味着该方法声明了它处理的是不安全的 Map,出错是正常的,需要外部系统确认自己处理 Map 的线程安全问题。如果你需要这个方法自己考虑同步问题,那么它至少需要在规范上限制传入的参数类型是线程同步的或者不可变的。 从你的描述看,调用方非常明确的告诉你传入的是一个没有线程同步还被多线程共享的 java.util.Map 对象。你赶紧把这个 BUG 提交上去吧,技术层面解决不了。 解释一下:只有多线程共享、并且线程不安全的 Map 对象,才有可能出现这边正在处理着,那边 put/remove 的情况。 |
40
luozic 2018-09-29 19:59:08 +08:00 via iPhone
这种多线程请求还不同步的,脑袋被踢了? 暂时可以抽象为,只有一个线程有取数据和移除数据的操作,其他线程都从提交到这个线程处理。
|
41
troywinter 2018-09-30 00:48:07 +08:00
我记得没错的话,你对 java 传参有什么误解,实现上来说,java will make a copy of this parameter,加 final 修饰并不能解决问题,final 只能禁止重新赋值,不能禁止修改这个对象。
|
42
otmoc 2018-10-07 14:05:05 +08:00
# 如果只是解决报错的问题的话
String[] keys = map.keySet().toArray(new String[map.size()]); for (String key : keys) { String value = map.get(key); if (value == null) { // do something } //do others something } # 如果不只是解决报错的话,那设计的接口有问题 |