链式线程安全集合线程安全吗?
如果我有以下声明:
Map<String, Map<String, Person>> families =
Collections.synchronizedMap(new HashMap<String, Map<String, Person>>());
如果我然后像这样链接调用:
families.get(lastName).put(firstName, new Person());
这个线程安全吗?对我来说,看起来两个地图中只有一个是同步的,但如果不经过外部同步地图就无法到达内部地图,所以我不确定......
编辑 到目前为止,两个答案都提出了很好的观点,非常感谢!但现在我在想如果我这样做怎么办:
families.put(lastName, Collections.synchronizedMap(new HashMap<String, Person>());
然后做了我的链式调用,整个链线程安全吗?是否有可能在 get(lastName)
和 put(firstName, new Person())
之间另一个线程可以获取内部映射?我在想,如果我想要整个链线程安全,我需要将它放在同步块内,但我也想知道这是否也有效......
If I have the following declaration:
Map<String, Map<String, Person>> families =
Collections.synchronizedMap(new HashMap<String, Map<String, Person>>());
If I then chain a call like so:
families.get(lastName).put(firstName, new Person());
Is this thread safe? To me it looks like only one of the two maps is synchronized but you cannot get to the inner map without going through the outer synchronized map so I am unsure...
EDIT
Excellent points made in both answers so far thanks a bunch! But now I am thinking what if I did this:
families.put(lastName, Collections.synchronizedMap(new HashMap<String, Person>());
then did my chained call, is that entire chain thread safe? Is it possible that between the get(lastName)
and put(firstName, new Person())
that another thread could get the inner map? I am thinking that if I want the entire chain thread safe I need to put it inside of a synchronized block, but I am also wondering if this will work as well...
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(3)
您将通过同步的外部映射获得内部映射,然后您可以用它做您想做的事情。所以这不是线程安全的。
如果您确实希望在这种情况下实现线程安全,我将创建一个包含映射映射的对象,然后您可以通过同步访问器来控制访问。我建议对于大多数组合集合的场景来说这是一个很好的实践。
You'll get the inner map through the synchronised outer map, but then you can do what you want with it. So that's not thread safe.
If you really want thread safety in this scenario, I'd create an object that contains the map of maps, and then you can control access via synchronising the accessors. I suggest this is a good practise for most scenarios where you compose collections of collections.
内部映射不是线程安全的。
如果其他线程
使用相同
lastName
,那么一个线程可能会获取内部映射,然后另一个线程获取内部映射,然后两者都调用同时放置
并破坏所有内容。使用
synchronizedMap
通常不足以实现正确的并发性 - 通常您希望显式锁定每个事务,而不是每个方法调用。The inner map is not thread-safe.
If some other thread does
with the same
lastName
, then it's possible for one thread to get the inner map, then the other thread gets the inner map, then both callput
at the same time and break everything.Using
synchronizedMap
is often not sufficient for correct concurrency - usually you want to explicitly lock around each transaction, rather than around each method call.完全有可能在调用外部 get() 和内部 put() 之间,其他线程也调用外部 get() 并获取相同的内部映射。但既然是同步的,无论如何应该是安全的。
问题是当你将一些东西放入外部地图时。线程如何确定是否需要创建新的内部映射?假设您有这样的代码:
现在,这绝对是不安全的,因为其他线程可能会同时执行相同的操作,因此您最终创建了两个内部映射,其中一个立即成为垃圾收集的候选者。
最好只同步与整个结构一起使用的所有方法,而不使用同步映射。它可能也会更快,因为您只需要一级同步。
It is perfectly possible that between calls to the outer get() and to the inner put() some other thread calls the outer get() too and gets the same inner map. But since it is synchronized, it should be safe anyway.
The problem is when you put something into the outer map. How does a thread determine if it is necessary to create a new inner map? Suppose you have a code like this:
Now, this is definitely unsafe as some other thread may do the same thing at the same time, so you end up creating two inner maps one of which becomes a candidate for garbage collecting right away.
It's much better to just synchronize all methods that work with the whole structure, and don't use synchronized maps. It will probably be faster too as you only need one level of synchronization.