我知道ArrayList
不是线程安全的,但我不确定这个的确切含义。
如果ThreadA
和ThreadB
都使用ArrayList
,哪些情况会导致问题并需要同步?
ThreadA
替换ThreadB
同时尝试访问的元素,假设您不关心ThreadB
是否获取旧元素或新元素。 答案 0 :(得分:3)
两个线程同时读取相同的索引
如果列表是由分叉ArrayList
和ThreadA
的线程构建的,那么多个线程可以从公共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)
如果在发布之前初始化列表,那么从列表中读取多个线程就没有问题。
只要您同时读取和写入列表,就需要同步访问它。