我知道ArrayList
并不是线程安全的,但是我不确定其确切含义。
对于同时使用ThreadA
的ThreadB
和ArrayList
,以下哪种情况会引起问题并需要同步?
两个线程同时读取相同的索引ThreadA
替换ThreadB
试图同时访问的元素,假设您不在乎ThreadB
是获取旧元素还是获取新元素。
最佳答案
两个线程同时读取相同的索引
如果列表是由分叉ArrayList
和ThreadA
的线程构造的,并且在分叉线程之前已完全构造并加载了列表,则可以从公共ThreadB
读取多个线程。
这样做的原因是,使用线程和派生该线程的线程的内存存在事前保证。例如,如果ThreadC
构建了ArrayList
,但在ThreadA
和ThreadB
被分叉之后,则不能保证A和B将完全看到ArrayList
-如果有的话。
如果不是这种情况,那么您将需要同步列表。见下文。
ThreadA更改ThreadB试图同时访问的元素,假设您不在乎ThreadB是获取旧元素还是获取新元素。
一旦您谈论了在并发设置中对该列表的修改,则必须在该列表上进行同步,否则无法保证将发布这些修改,并且有可能部分发布该列表,这可能会导致数据异常。正如@Marko所说,其内部状态可能不一致。
您可以使用专为少量更新和多次读取而设计的CopyOnWriteArrayList
,使用Collections.synchronizedList(...)
来保护列表,可以始终在synchronized
块中访问列表(对于所有写入和读取),或者您可以切换为使用并发集合,例如ConcurrentSkipList
之类的东西。
ThreadA更改ThreadB试图同时访问的元素
这有点模棱两可。例如,如果您要谈论的是将对象存储在列表中,然后更改碰巧要存储在列表中的对象,那么列表上就不会出现同步问题,但是与对象的同步问题。如果列表的数据没有变化,那就可以了。但是,如果需要保护该对象,则需要该对象中的AtomicReference<YourObject>
,volatile
字段列表或进行其他同步,以确保更改在线程之间发布。