ArrayList并发访问

时间:2014-03-10 13:24:55

标签: java arraylist concurrency

我知道ArrayList不是线程安全的,但我不确定这个的确切含义。

如果ThreadAThreadB都使用ArrayList,哪些情况会导致问题并需要同步?

  1. 两个线程同时读取相同的索引
  2. ThreadA替换ThreadB同时尝试访问的元素,假设您不关心ThreadB是否获取旧元素或新元素。

3 个答案:

答案 0 :(得分:3)

  

两个线程同时读取相同的索引

如果列表是由分叉ArrayListThreadA的线程构建的,那么多个线程可以从公共ThreadB 读取list是在分叉线程之前完全构造和加载的。

这样做的原因是,有一个先前发生的保证,一个线程和分叉它的线程的内存。例如,如果ThreadC构建ArrayList ThreadA之后的ThreadB分叉,则无法保证A和B将完全看到ArrayList - 如果有的话。

如果不是这种情况,则需要同步列表。见下文。

  

ThreadA更改ThreadB尝试同时访问的元素,假设您不关心ThreadB是否获取旧元素或新元素。

一旦您在并发设置中讨论对列表的修改,那么您必须在该列表上进行同步,否则无法保证将发布修改,并且可能会部分发布列表,这可能会导致数据异常。正如@Marko所说,它的内部状态可能不一致。

您可以使用专为少量更新和多次阅读而设计的CopyOnWriteArrayList,使用Collections.synchronizedList(...)使列表受到保护,您可以始终在synchronized块中访问列表(对于所有写入读取),或者您可以切换到使用并发集合,例如ConcurrentSkipList或其他。

  

ThreadA更改ThreadB尝试同时访问的元素

这有点含糊不清。如果您正在谈论,例如,在列表中存储对象,然后更改恰好存储在列表中的对象,那么您将不会在列表上出现同步问题但是您将与对象有同步问题。如果列表的数据没有变化那么它就没问题了。但是,如果需要保护对象,则需要在对象中列出AtomicReference<YourObject>volatile字段或其他同步,以确保在线程之间发布更改。

答案 1 :(得分:2)

在您的问题中,我看到强调同时访问。

并发访问的问题与同时性几乎没有关系。更强烈地说,即使你确保没有同时访问,你仍然离一个线程安全的程序还很远。

您特定要点的答案:

  

1)两个线程同时读取相同的索引

只要您的线程只读取而且从不写入,无论是否同时发生,您都是安全的。

  

2)ThreadA更改ThreadB同时尝试访问的元素,假设您不关心ThreadB是否获得旧元素或新元素。

无论写作是否与阅读同时发生,你都有麻烦。你不仅有可能看到陈旧的价值观;你可以看到一个完全损坏的List对象(它的内部状态不一致)。

如果任何线程更改了列表,则需要同步。

有关更多信息,我强烈建议您熟悉Java内存模型,最好是从Java语言规范的相应部分开始。

答案 2 :(得分:0)

如果在发布之前初始化列表,那么从列表中读取多个线程就没有问题。

只要您同时读取和写入列表,就需要同步访问它。