我正在与existing具有 ConcurrentHashMap 形式的对象存储的代码。映射内存储了可供多个线程使用的可变对象。根据设计,没有两个线程会尝试同时修改一个对象。我关心的是线程之间修改的可见性。
目前,对象的代码在“setter”上具有同步(由对象本身保护)。 “getter”上没有同步,成员也不是易失性的。对我来说,这意味着无法保证可见性。然而,当一个对象被修改时re-put回到地图(put()
再次调用方法,相同的键)。这是否意味着当另一个线程将对象从映射中拉出时,它会看到修改?
我在 stackoverflow 上对此进行了研究,在JCIP http://jcip.net/,以及 java.util.concurrent 的包描述中。我认为我基本上让自己感到困惑......但导致我问这个问题的最后一根稻草来自包装说明,它指出:
将对象放入任何并发集合之前的线程中的操作发生在另一个线程中从集合中访问或删除该元素之后的操作之前。
关于我的问题,“操作”是否包括在 re-put() 之前对存储在地图中的对象进行的修改?如果所有这些确实导致跨线程的可见性,那么这是一种有效的方法吗?我对线程比较陌生,非常感谢您的评论。
Edit:
谢谢大家的回复!这是我在 StackOverflow 上的第一个问题,它对我很有帮助。
我必须和ptomli https://stackoverflow.com/users/134894/ptomli的答案,因为我认为它最清楚地解决了我的困惑。也就是说,在这种情况下,建立“发生之前”关系并不一定会影响修改可见性。关于我在文中描述的实际问题,我的“标题问题”的结构很差。ptomli https://stackoverflow.com/users/134894/ptomli现在的答案与我读到的内容一致JCIP http://jcip.net/:“为了确保所有线程都能看到共享可变变量的最新值,读取和写入线程必须在公共锁上同步”(第 37 页)。将对象重新放回到映射中不会为修改插入的对象的成员提供此公共锁。
我很欣赏所有关于改变的技巧(不可变的对象等),并且我完全同意。但对于这种情况,正如我提到的,由于仔细的线程处理,没有并发修改。一个线程修改一个对象,然后另一个线程读取该对象(CHM 是对象传送器)。我认为 CHM 不足以确保稍后执行的线程将看到我提供的情况下第一次执行的修改。然而,我认为你们很多人正确回答了标题问题.
你打电话concurrHashMap.put
每次写入对象后。但是您没有指定您还调用concurrHashMap.get
每次阅读之前。这是必要的。
所有形式的同步都是如此:您需要在两个线程中都有一些“检查点”。只同步一个线程是没有用的。
我还没有检查 ConcurrentHashMap 的源代码来确保put
and get
触发之前发生的事件,但它们应该这样做是合乎逻辑的。
但是,即使您同时使用,您的方法仍然存在问题put
and get
。当您修改一个对象并且该对象在它被修改之前被其他线程使用(处于不一致的状态)时,就会出现问题put
。这是一个微妙的问题,因为您可能认为旧值会被读取,因为它还没有被读取。put
但这不会造成问题。问题是,当您不同步时,不能保证获得一致的旧对象,而是行为未定义。 JVM 可以随时更新其他线程中对象的任何部分。仅当使用某些显式同步时,您才能确定在线程之间以一致的方式更新值。
你可以做什么:
(1) 同步代码中各处对象的所有访问(getter 和 setter)。请小心设置器:确保不能将对象设置为不一致的状态。例如,在设置名字和姓氏时,拥有两个同步设置器是不够的:您必须同时获取这两个操作的对象锁。
or
(2) 当你put
地图中的对象,放置深层副本而不是对象本身。这样其他线程永远不会读取处于不一致状态的对象。
EDIT:
我刚刚注意到
目前对象的代码在“setters”上具有同步
(由对象本身保护)。上没有同步
“吸气剂”的成员也不是不稳定的。
不是很好。正如我上面所说,仅在一个线程上同步根本就不是同步。您可能会在所有编写器线程上同步,但谁在乎呢,因为读者不会获得正确的值。
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)