Java ArrayList / String / atomic变量读取线程是否安全?

我一直在考虑阅读和阅读,但可以找到绝对权威的答案。

我有几个深层数据结构,由包含ArrayLists,Strings和原始值的对象组成。 我可以保证这些结构中的数据不会改变(没有线程会对列表进行结构更改,更改引用,更改基元)。

我想知道在这些结构中读取数据是否是线程安全的; 即从对象递归读取变量是否安全,迭代ArrayLists等以从多个线程中的结构中提取信息而不同步?

它不安全的唯一原因是,如果一个线程正在写入一个字段,而另一个线程同时从该字段读取。 如果数据没有变化,则不存在竞争条件 。 使对象不可变是保证它们是线程安全的一种方法。 首先阅读IBM的这篇文章。

ArrayList的成员不受任何内存障碍的保护,因此无法保证在线程之间可以看到对它们的更改。 即使对列表进行的唯一“更改”是其构造,这也适用。

线程之间共享的任何数据都需要“内存屏障”以确保其可见性。 有几种方法可以实现这一目标。

首先,在构造函数完成后,任何线程都可以看到在构造函数中声明为final并初始化的任何成员。

对所有已声明为volatile成员的更改对所有线程都可见。 实际上,写入从任何高速缓存“刷新”到主存储器,任何访问主存储器的线程都可以看到它。

现在它变得有点棘手。 该线程写入volatile变量之前 ,线程所做的任何写操作也都会被刷新。 同样,当一个线程读取一个volatile变量时,它的缓存被清除,后续的读取可能会从主内存中重新填充它。

最后, synchronized块就像易失性读写一样,具有增加的primefaces性质量。 获取监视器后,将清除线程的读取缓存。 释放监视器后,所有写入都将刷新到主存储器。

使这项工作的一种方法是让填充共享数据结构的线程将结果分配给volatile变量(或AtomicReference ,或其他合适的java.util.concurrent对象)。 当其他线程访问该变量时,它们不仅保证获得该变量的最新值,而且还保证线程在将值赋给变量之前对数据结构所做的任何更改。

如果数据在创建后永远不会被修改,那么你应该没问题,读取将是线程安全的。

为了安全起见,您可以使所有数据成员“最终”并使所有访问函数尽可能重入; 这可以确保线程安全,并且如果您将来更改它,可以帮助保持代码线程安全。

一般而言,尽可能多地使成员“最终”有助于减少错误的引入,因此许多人提倡将其作为Java最佳实践。

正如对其他人的答案的补充:如果您确定需要同步数组列表,可以调用Collections.synchronizedList(myList),它将返回一个线程安全的实现。

我看不出如何使用多个线程从ArrayLists,Strings和原始值读取应该是什么问题。

只要您只是阅读,就不需要同步。 对于字符串和原语,它肯定是安全的,因为它们是不可变的。 对于ArrayLists,它应该是安全的,但我没有权限。

不要使用java.util.Vector ,如果它们确实是不可修改的,请使用java.util.Collections.unmodifiableXXX()包装器,这将保证它们不会更改,并将强制执行该合同。 如果要修改它们,则使用java.util.Collections.syncronizedXXX() 。 但这只能保证内部线程安全。 使变量final也将有助于编译器/ JIT进行优化。