所以在我写的各种好玩的程序中,我都遇到过并发修改异常。
在解决这个问题的天真尝试中,我使用了原子而不是某种并发集合。我有点熟悉为什么这会导致错误。本质上,ArrayList 的各个元素不是同步的,可以由不同的线程随意修改。
使用
AtomicReference
存储对象(例如集合)不足以使其成为线程安全的。事实上,如果您放入 AtomicReference
中的对象不像 ArrayList
那样是线程安全的,那么如果我们有多个线程尝试同时修改其状态,那么使用它仍然是不安全的。所以好的方法仍然是在你的AtomicReference
中放入一个不可变的对象,这样它的状态就不能再被多个线程修改。例如,如果是集合,您可以使用 Collections.unmodifiable*
类型的方法,例如用于列表的 Collections.unmodifiableList(List)
,以便将集合的不可变版本放入 AtomicReference
中。
如果你需要线程安全的集合,你应该看看包中的类
java.util.concurrent
,你会发现集合本身是线程安全的。例如,如果您主要阅读并且很少修改您的List
,则可以使用线程安全且高效的列表CopyOnWriteArrayList。
我认为您混淆了 ConcurrentModification 的含义......
最常见的情况是当您迭代集合并在循环中修改它时。
例如,如果您执行以下操作
public static void main(String[] args) {
List<String> l = new LinkedList<>();
for(int i=0; i < 100; i++) {
l.add("banana"+i);
}
for (String s : l) {
if("banana10".equals(s)) {
l.remove(s);
}
}
}
...这会给你一个 ConcurrentModificationException。请注意,我还没有生成任何线程。
正确的做法如下:
public static void main(String[] args) {
List<String> l = new LinkedList<>();
for(int i=0; i < 100; i++) {
l.add("banana"+i);
}
for (Iterator<String> iterator = l.iterator(); iterator.hasNext();) {
String s = iterator.next();
if("banana10".equals(s)) {
iterator.remove();
}
}
}
注意在循环遍历集合时使用迭代器来修改集合。
所以,我不认为你有并发问题!
如果你想让你的集合线程安全,你需要看看线程安全的语义。如果你想允许多个线程访问同一个集合,ConcurrentList 将是一个很好的方法。如果您想要一个作为整体原子设置的列表引用,您可以使用原子引用。