我有一个需要由多个线程调用的List
。将只有一个作家,这个作家将添加元素,不做任何其他事情。永远不会删除或修改元素。许多并发读者线程会调用list.size()
和list.get(index)
。
我们必须假设有时内部数组需要在添加元素时增长。
我可以使用普通的ArrayList
,或者我是否需要实现一些花哨的并发结构以避免异常?
答案 0 :(得分:3)
如果您的读者都需要阅读brew install git
的相同状态,这意味着作者无法在所有读者之间书写,那么是的,您需要使用git
或处理这种情况的一些更复杂的方法。
如果您没有充分利用内存,请不要介意读者是否始终拥有最新数据,而您的唯一目标只是避免例外,请考虑List
,避免写/迭代冲突的异常。当您写入此ReadWriteLock
时,它会将新的结果作为完成结果,但当前使用CopyOnWriteArrayList
的读者会继续阅读“旧”(预写)而不是。
答案 1 :(得分:1)
首先,关于ArrayList的文档说它不是线程安全的 - 所以我不建议在这种情况下使用它。此外,即使这在理论上起作用,你也有一个问题就是确保维护这个程序的人记得严格的限制,如果他们不这样做的话,你就会强行阅读和写作。也就是说,从理论的角度来看,你的问题非常有趣,所以我想我会看看。
首先要注意的是,任何读者都试图通过迭代器访问列表,然后迟早读取会因ConcurrentModificationException而失败。这是因为跟踪了对列表的每个修改,如果迭代器检测到迭代器之外的修改,则迭代器会快速失败。
那就是说,你没有指定在原始问题中使用迭代器的必要性,那么如果你只想通过添加和读取get()和size()进行追加,会发生什么?在这种情况下,我仍然认为您可能会偶尔看到失败,因为内部列表会将当前大小与后备数据数组一起存储,并且这两个变量都没有标记为volatile。因此(并且由于Java的内存保证),我认为理论上可以让其中一个读者线程看到更新的大小变量,而数据仍未更新。在这种情况下,您可能会返回返回的垃圾数据,甚至是indexOutOfBounds。
答案 2 :(得分:1)
如果您打开使用第三方库,Eclipse Collections有一个名为MultiReaderFastList
的类型,它支持多个读者和一个编写者。它引发iterator
。如果需要迭代,请查看使用众多内部迭代方法之一,这将安全地获取读锁定并在迭代完成后释放它。如果您需要编写命令式迭代代码,则有两个名为withReadLockAndDelegate
和withWriteLockAndDelegate
的方法,允许您在代码块之前和之后使用iterator
安全锁定。
注意:我是Eclipse Collections的提交者。
答案 3 :(得分:-1)
我的建议是使用信号量来避免任何异常,这样你就可以随时控制哪个读者正在阅读列表