rya*_*000 8 java concurrency concurrenthashmap
我正在使用具有ConcurrentHashMap形式的对象存储的现有代码.在地图内存储可变对象,由多个线程使用.没有两个线程尝试通过设计一次修改对象.我关注的是线程之间修改的可见性.
目前,对象的代码在"setter"上具有同步(由对象本身保护)."getters"上没有同步,成员也不易变形.对我而言,这意味着无法保证可见性.但是,当对象被修改时,它会被重新放回到地图中(该put()方法再次被调用,相同的键).这是否意味着当另一个线程将对象拉出地图时,它会看到修改?
我已经在stackoverflow,JCIP和java.util.concurrent的包描述中对此进行了研究.我基本上把自己搞糊涂了......但是最后一根稻草让我问这个问题来自于包装描述,它说:
在将对象放入任何并发集合之前的线程中的操作发生在从另一个线程中的集合访问或移除该元素之后的操作之前.
关于我的问题,"动作"是否包括在重新放置()之前对地图中存储的对象的修改?如果所有这些确实导致跨线程的可见性,这是一种有效的方法吗?我对线程比较新,感谢你的评论.
编辑:
谢谢大家的回复!这是我在StackOverflow上的第一个问题,它对我非常有帮助.
我必须接受ptomli的回答,因为我认为它最清楚地解决了我的困惑.也就是说,在这种情况下,建立"先发生过"关系并不一定会影响修改可见性.关于我在文中描述的实际问题,我的"标题问题"构造得很差.ptomli的答案现在jives与我在读JCIP:'为了确保所有线程共享见可变变量的最先进的日期值,读取和写入线程必须在一个共同的锁同步’(第37页).将对象重新放回到映射中不会为修改插入对象的成员提供此公共锁.
我很欣赏所有改变的提示(不可变对象等),我全心全意地同意.但对于这种情况,正如我所提到的,由于仔细的线程处理,没有并发修改.一个线程修改一个对象,另一个线程稍后读取该对象(CHM是对象传送器).我认为CHM不足以确保后来执行的线程会在我提供的情况下看到第一个修改.但是,我想很多人都正确地回答了标题问题.
concurrHashMap.put每次写入对象后调用.但是,您没有指定concurrHashMap.get在每次读取之前也调用.这是必要的.
所有形式的同步都是如此:您需要在两个线程中都有一些"检查点".仅同步一个线程是没用的.
我没有检查ConcurrentHashMap的源代码,以确保put并get触发事先发生,但它们应该是合乎逻辑的.
但是,即使你同时使用put和,你的方法仍然存在问题get.修改对象时会发生问题,而另一个线程使用它(处于不一致状态)put.这是一个微妙的问题,因为您可能会认为旧值将被读取,因为它put尚未存在且不会导致问题.问题是,当您不进行同步时,不能保证获得一致的旧对象,而是行为未定义.JVM可以随时更新其他线程中对象的任何部分.只有在使用某些显式同步时,您确定要跨线程以一致的方式更新值.
您可以做什么:
(1)将所有访问(getter和setter)同步到代码中的对象.小心setter:确保不能将对象设置为不一致状态.例如,在设置名字和姓氏时,有两个同步的setter是不够的:您必须同时获得两个操作的对象锁定.
或者
(2)当你put在地图中的一个对象时,放一个深拷贝而不是对象本身.这样,其他线程永远不会读取处于不一致状态的对象.
编辑:
我刚注意到
目前,对象的代码在"setter"上具有同步(由对象本身保护)."getters"上没有同步,成员也不易变形.
这个不好.正如我上面所说,仅在一个线程上进行同步根本就没有同步.您可能会在所有编写器线程上进行同步,但是由于读者无法获得正确的值,因此他们会关心.
我认为这已经在几个答案中说过,但总结一下
如果你的代码去了
那么put提供的"before-before"将保证所有mutate调用在put之前执行.这意味着任何后续获取都将保证看到这些更改.
您的问题是对象的实际状态不是确定性的,因为如果实际的事件流是
然后无法保证对象的状态在线程2中的状态.它可能会看到具有第一个setter提供的值的对象,或者它可能没有.
不可变副本将是最好的方法,因为只发布完全一致的对象.使各种setter同步(或底层引用volatile)仍然不允许您发布一致状态,它只是意味着对象将始终在每次调用时看到每个getter的最新值.
我认为您的问题更多地与您在地图中存储的对象以及它们对并发访问的反应有关,而不是与并发地图本身有关。
如果您存储在映射中的实例具有同步变元,但没有同步访问器,那么我不明白它们如何能够如所描述的那样是线程安全的。
排除Map等式并确定您存储的实例本身是否是线程安全的。
然而,当一个对象被修改时,它会被重新放回映射中(再次调用 put() 方法,相同的键)。这是否意味着当另一个线程将对象从映射中拉出时,它会看到修改?
这就是混乱的例证。重新放入 Map 的实例将由另一个线程从 Map 中检索。这就是并发map的保证。这与存储实例本身状态的可见性无关。
| 归档时间: |
|
| 查看次数: |
3095 次 |
| 最近记录: |