如果我有以下声明:
Map<String, Map<String, Person>> families =
Collections.synchronizedMap(new HashMap<String, Map<String, Person>>());
如果我然后链接一个这样的电话:
families.get(lastName).put(firstName, new Person());
这个线程安全吗?对我来说,看起来两个地图中只有一个是同步的,但如果不通过外部同步地图就无法到达内部地图,所以我不确定......
EDIT 到目前为止,两个答案都有优秀的分数!但现在我在想如果我这样做了:
families.put(lastName, Collections.synchronizedMap(new HashMap<String, Person>());
然后做了我的链接调用,是整个链线程安全吗?在get(lastName)
和put(firstName, new Person())
之间,另一个线程可能获得内部地图吗?我想如果我想要整个链线程安全我需要将它放在一个synchronized块中,但我也想知道这是否也能正常工作......
答案 0 :(得分:4)
你将通过同步的外部地图获得内部地图,但随后你可以用它做你想做的事情。所以不线程安全。
如果你真的想在这个场景中使用线程安全,我会创建一个包含地图映射的对象,然后你可以通过同步访问器来控制访问。我建议这是一个很好的做法,适用于你撰写馆藏集合的大多数场景。
答案 1 :(得分:4)
内部地图不线程安全。
如果某个其他线程
families.get(lastName).put(firstName, new Person());
使用相同的 lastName
,然后一个线程可以获取内部地图,然后另一个线程获取内部地图,然后两个线程都调用put
同一时间,打破一切。
使用synchronizedMap
通常不足以实现正确的并发 - 通常您希望显式锁定每个事务,而不是围绕每个方法调用。
答案 2 :(得分:1)
完全有可能在调用外部get()和内部put()之间,其他一些线程也调用外部get()并获得相同的内部映射。但由于它是同步的,所以无论如何都应该是安全的。
问题是当你把东西放到外部地图上时。线程如何确定是否有必要创建新的内部地图?假设你有这样的代码:
if (!families.containsKey(lastName)) {
families.put(lastName, Collections.synchronizedMap(new HashMap<String, Person>());
}
现在,这肯定是不安全的,因为其他一些线程可能会同时执行相同的操作,因此您最终会创建两个内部映射,其中一个成为垃圾收集的候选者。
只需同步所有与整个结构一起使用的方法,并且不使用同步映射,这样会好得多。它可能会更快,因为您只需要一个级别的同步。