ArrayList并发访问

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

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

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

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

如果列表是由分叉ThreadAThreadB的线程构造的,并且列表在线程被分叉之前完全构造和加载,那么多个线程可以从公共ArrayList读取。

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

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

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

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

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

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

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

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

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

您的具体要点的答案:

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

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

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

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

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

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

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

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