这是我对不可变数据结构(尤其是不可变映射)的了解,如果我错了,请纠正我:
如果有两个线程t1
和t2
向不变映射插入相同的键。在t1
完成其工作并更新共享引用之前,t2
开始其工作,并接收尚未插入其密钥的旧地图,并继续插入相同的密钥。现在,映射的最终版本取决于最后更新共享引用的线程。在这两种情况下,都有一个废弃的键值,垃圾回收器会很快清除该键值。
我的问题是,当两个线程将相同的键插入一个不变的映射(该引用在线程之间共享)时,如何解决该问题?还是首先使用共享引用是错误的?
答案 0 :(得分:3)
关于术语:有两种集合称为“不可变的”。
ImmutableMap
)。您可能正在问第二种“不变性”。
我想你有这样的东西:
private CopyOnWriteTree tree;
void insertValue(Value value) {
tree = tree.insert();
}
在这种情况下,使用共享引用似乎没有用。
如果由多个线程使用对包含insertValue()
字段的对象的相同共享引用来调用tree
,那么最好的办法就是使用{{ 1}},synchronized
或volatile
,但其中之一将丢失。 (请注意,这里不是参考更新原子性;问题是没有这种同步,甚至不能保证一个线程能够看到其他线程所做的更新。)
写时复制集合适用于每个线程处理自己的数据副本并在工作完成后将其丢弃的情况。
如果您需要同时保留两个更新,请不要使用写时复制集合。而是尝试一些并发集合(AtomicReference
或类似的东西)。
PS。我刚刚意识到问题出在Scala集合上,而我的回答就像是关于Java。尽管如此,逻辑仍然相同,内存模型也相同。
答案 1 :(得分:2)
问题分为两部分。第一个问题是
如果两个线程写入相同的共享引用会发生什么?
答案是它可能会损坏,您不应该这样做。如果要让两个线程写入相同的引用,则需要使用org.springframework.messaging.MessageDeliveryException: failed to send Message to channel
之类的同步机制来保护它。
您需要问的第二个问题是
两个线程同时读取一个不变的对象总是安全的吗?
答案是否定的。
不可变对象可能包含可变数据,因此可能不是线程安全的。您需要查看有关对象的规范,以确定它是否是线程安全的。