我一直在考虑这个& amp;阅读,但可以找到一个绝对权威的答案。
我有几个由包含ArrayLists,Strings& amp;的对象组成的深度数据结构。原始价值观。我可以保证这些结构中的数据不会改变(没有线程会对列表进行结构更改,更改引用,更改基元)。
我想知道在这些结构中读取数据是否是线程安全的;即从对象递归读取变量是否安全,迭代ArrayLists等以从多个线程中的结构中提取信息而不同步?
答案 0 :(得分:13)
它不安全的唯一原因是,如果一个线程正在写入字段而另一个线程同时从该字段读取。如果数据没有变化,则不存在race condition。使对象不可变是保证它们是线程安全的一种方法。首先阅读IBM的this文章。
答案 1 :(得分:5)
ArrayList
的成员不受任何内存障碍的保护,因此无法保证在线程之间可以看到对它们的更改。即使对列表进行的唯一“更改”是其构造,这也适用。
线程之间共享的任何数据都需要“内存屏障”以确保其可见性。有几种方法可以实现这一目标。
首先,在构造函数完成后,任何被声明为final
并在构造函数中初始化的成员都可以看到。
所有线程都可以看到对声明为volatile
的任何成员的更改。实际上,写入从任何高速缓存“刷新”到主存储器,任何访问主存储器的线程都可以看到它。
现在它变得有点棘手了。线程在线程写入易失性变量之前所做的任何写入也会被刷新。同样,当一个线程读取一个volatile变量时,它的缓存被清除,随后的读取可能会从主内存重新填充它。
最后,synchronized
块就像一个易失性读和写,具有增加的原子性质量。获取监视器后,将清除线程的读取缓存。释放监视器后,所有写入都将刷新到主存储器中。
使这项工作的一种方法是让填充共享数据结构的线程将结果分配给volatile
变量(或AtomicReference
或其他合适的java.util.concurrent
对象)。当其他线程访问该变量时,不仅保证获得该变量的最新值,而且还保证线程在将值赋给变量之前对数据结构所做的任何更改。
答案 2 :(得分:3)
如果数据在创建后从未被修改过,那么你应该没问题,读取将是线程安全的。
为了安全起见,您可以使所有数据成员“最终”并使所有访问功能在可能的情况下重入;这可以确保线程安全,并且如果您将来更改它,可以帮助保持代码线程安全。
总的来说,让尽可能多的成员“最终”有助于减少错误的引入,因此很多人都认为这是Java的最佳实践。
答案 3 :(得分:2)
作为其他人答案的附录:如果您确定需要同步数组列表,可以调用Collections.synchronizedList(myList),这将返回一个线程安全的实现。
答案 4 :(得分:2)
我无法看到如何从ArrayLists 读取,使用多个线程的字符串和原始值应该是任何问题。
只要您只是阅读,就不需要同步。对于字符串和原语,它肯定是安全的,因为它们是不可变的。对于ArrayLists,它应该是安全的,但我没有权限。
答案 5 :(得分:1)
不使用java.util.Vector
,如果真的不可修改则使用java.util.Collections.unmodifiableXXX()
包装,这将保证它们不会更改,并会强制执行该合同。如果要修改它们,请使用java.util.Collections.syncronizedXXX()
。但这只能保证内部线程安全。使变量final
也可以帮助编译器/ JIT进行优化。