带有原子替换的java线程安全可序列化集合
当多个线程通过RMI访问同一台服务器时,我的程序面临一个问题。服务器包含一个列表作为缓存,并执行一些昂贵的计算,有时会更改该列表。计算完成后,列表将被序列化并发送到客户端
第一个问题:如果列表在序列化时发生更改(例如,由另一个请求某些数据的客户机更改),则会(可能)抛出ConcurrentModificationException
,从而导致RMI调用的EOFException
在客户端进行反序列化
因此,我需要一种列表结构,这种结构对于序列化来说是“稳定的”,同时可能会被另一个线程更改
我们尝试的解决方案:
- 常规ArrayList/Set-由于并发而无法工作
- 在每次序列化之前深度复制整个结构-faaar太贵了
CopyOnWriteArrayList
也很昂贵,因为它复制了列表和
揭示第二个问题:我们需要能够以原子方式替换列表中当前不安全的任何元素(先删除,然后添加(这更昂贵)),或者只通过锁定列表,从而只按顺序执行不同的线程
因此,我的问题是:
Do you know of a
Collection
implementation which allows us toserialize
the Collection thread-safe while other Threads modify itand
which contains some way ofatomically replacing
elements?A bonus would be if the list would
not
need to becopied
before serialization! Creating a snapshot for every serialization would be okay, but still meh :/
问题说明(C=compute,A=add to list,R=remove from list,S=serialize)
Thread1 Thread2
C
A
A C
C A
S C
S R <---- Remove and add have to be performed without Thread1 serializing
S A <---- anything in between (atomically) - and it has to be done without
S S blocking other threads computations and serializations for long
S and not third thread must be allowed to start serializing in this
S in-between state
S
# 1 楼答案
我最初错误的想法是
CopyOnWriteArrayList
是个坏主意,因为它复制了所有东西。当然,它只执行浅层复制,只复制引用,而不是深度复制,同时复制所有对象因此,我们显然选择了
CopyOnWriteArrayList
,因为它已经提供了许多所需的功能。唯一剩下的问题是replace
,它甚至变得更复杂,成为addIfAbsentOrReplace
我们尝试了
CopyOnWriteArraySet
,但这不符合我们的需要,因为它只提供addIfAbsent
。但在我们的例子中,我们有一个名为C
的类c1
的实例,我们需要存储它,然后用更新的新实例c2
替换它。当然,我们覆盖equals
和hashCode
。现在,我们必须选择是否希望等式返回true
或false
来表示两个只有极小差异的对象。两种选择都不起作用,因为true
意味着对象是相同的,集合甚至不会费心添加新对象c2
,因为c1
已经存在false
意味着c2
将被添加,但c1
将不会被删除因此
CopyOnWriteArrayList
。这份清单已经提供了一个解决方案这有点符合我们的需要。它允许我们通过自定义比较替换所需的对象
我们以以下方式利用它:
# 2 楼答案
最简单的解决方案是暗示到
ArrayList
的外部同步,可能通过读写锁实现,如下所示:提供您喜欢的任何操作,只需正确使用读锁或写锁即可