我有存储许多对象的ArrayLists,并且经常在ArrayLists中添加和删除对象。一个线程对数据结构进行操作,每20ms左右更新一次ArrayList的对象。另一个线程遍历ArrayLists并使用它们的元素绘制对象(也是每20-30ms)。
如果使用for循环遍历ArrayLists,则IndexOutOfBoundsExceptions比比皆是。如果使用迭代器遍历ArrayLists,则ConcurrentModificationExceptions比比皆是。像这样同步ArrayLists:
List list = Collections.synchronizedList(new ArrayList());
synchronized(list) {
//use iterator for traversals
}
没有例外,但性能大幅下降。有没有办法遍历这些ArrayLists而没有抛出异常,并且没有性能消耗?
谢谢!
答案 0 :(得分:4)
解决此问题的一个好方法是使线程在列表的不同副本上工作。但是,CopyOnWriteArrayList
不适合这里,因为它会在每次修改时创建副本,但在您的情况下,最好不要频繁地创建副本。
因此,您可以手动实现它:第一个线程创建更新列表的副本并通过volatile
变量发布它,第二个线程使用此副本(我假设第一个线程仅修改列表,而不是其中的对象):
private volatile List publicList;
// Thread A
List originalList = ...;
while (true) {
modifyList(originalList); // Modify list
publicList = new ArrayList(originalList); // Pusblish a copy
}
// Thread B
while (true) {
for (Object o: publicList) { // Iterate over a published copy
...
}
}
答案 1 :(得分:2)
您是否尝试过使用Iterator
并使用CopyOnWriteArrayList?保证不会抛出ConcurrentModificationException
。
来自Oracle javadocs(强调添加):
ArrayList的线程安全变体 所有变异操作(添加, 设置,等等)由实现 制作基础的新副本 阵列。
这通常成本太高,但可能 比替代品更有效率 当遍历操作时 超过突变,并且是有用的 什么时候你不能或不想 同步遍历,但需要 排除并发之间的干扰 线程。 “快照”样式迭代器 方法使用对状态的引用 在该点的数组 迭代器已创建。这个数组从不 在生命周期中的变化 迭代器,所以干扰是 不可能和迭代器是 保证不扔 ConcurrentModificationException的即可。该 迭代器不会反映添加内容, 从那以后删除或更改列表 迭代器已创建。 改变元素的操作 迭代器本身(删除,设置和 add)不受支持。这些方法 抛出UnsupportedOperationException。
答案 2 :(得分:2)
如果在迭代之前将ArrayList复制到新变量中呢?这样,您只需要同步副本块而不是列表的整个迭代。
答案 3 :(得分:1)
创建CopyOnWriteArrayList类是为了解决这个问题。
答案 4 :(得分:1)
您可以使用CopyOnWriteArrayList,它不会获得ConcurrentModificationException,也不需要同步,或者您可以执行类似的操作。
List list = Collections.synchronizedList(new ArrayList());
List copy;
// lock the list for the minimal amount of time.
synchronized(list) {
copy = new ArrayList(list);
}
// use the copy of the array list.
BTW CopyOnWriteArrayList看起来像
List list = new CopyOnWriteArrayList();
// use the list.
答案 5 :(得分:1)
尽管CopyOnWriteArrayList为读者提供了最大的性能,但如果写入频繁,则会出现写入器性能问题。
如果您的访问模式只是通过迭代器而您没有进行任何随机访问,那么使用队列可能是更好的选择,因为您可以使用ConcurrentLinkedQueue之类的东西。请参阅示例JAVA: Concurrency control for access to list in java。