我有疑问。第一个是为什么当我们运行这个函数时,我们有ConcurrentModificationException?
public static void testList() {
List<String> list = new ArrayList<String>();
list.add("str3");
for (String st : list) {
if (st.equalsIgnoreCase("str3")) {
list.remove("str3");
}
}
System.out.println(list);
}
我的事,因为使用Iterator(检查modificationsCount
)增强了,但我要确定。这是例外的原因。
第二个问题是我是否使用Collections.synchronizedList(new LinkedList<Something>());
我可以使用2个或更多增强的循环?例如,我必须使用线程,并且在某个时候第一个从集合中删除元素,并且在某个时候第二个元素在集合中添加元素。即使在使用迭代器(使用迭代器增强的东西)的时候也应该是线程保存的东西
提前谢谢。
答案 0 :(得分:3)
如果您在迭代列表时从列表中删除元素,则会得到ConcurrentModificationException
,就像您在上面的示例中所做的那样。为避免这种情况,您应该使用显式迭代器而不是增强型for
循环,并在找到必须删除的元素时在迭代器而不是列表上调用remove()
:
for (Iterator<String> i = list.iterator(); i.hasNext(); ) {
String st = i.next();
if (st.equalsIgnoreCase("str3")) {
// Remove the element that the iterator is currently pointing to
i.remove();
}
}
仅使用Collections.synchronizedList(...)
包装列表并不会使得您可以同时在列表上迭代多个线程,其中一个线程正在从列表中删除元素。执行此操作时,您仍会获得ConcurrentModificationException
。使用Collections.synchronizedList(...)
包装列表将使列表中的各个方法同步,但多个方法没有同步。
您必须确保如果一个线程正在从列表中删除元素,则没有其他线程在列表上进行迭代,方法是正确同步您自己的迭代和删除元素的方法。
答案 1 :(得分:1)
第一个问题,是的。如果列表被枚举,则修改列表是常见的例外。
第二个问题,是的。但是你需要为你的每个for循环使用synchronized
块
答案 2 :(得分:1)
Iterator未在SynchronizedCollection中同步。我附加了SynchronizedCollection代码。
static class SynchronizedCollection<E> implements Collection<E>, Serializable {
private static final long serialVersionUID = 3053995032091335093L;
final Collection<E> c; // Backing Collection
final Object mutex; // Object on which to synchronize
SynchronizedCollection(Collection<E> c) {
this.c = Objects.requireNonNull(c);
mutex = this;
}
SynchronizedCollection(Collection<E> c, Object mutex) {
this.c = Objects.requireNonNull(c);
this.mutex = Objects.requireNonNull(mutex);
}
public int size() {
synchronized (mutex) {return c.size();}
}
public boolean isEmpty() {
synchronized (mutex) {return c.isEmpty();}
}
public boolean contains(Object o) {
synchronized (mutex) {return c.contains(o);}
}
public Object[] toArray() {
synchronized (mutex) {return c.toArray();}
}
public <T> T[] toArray(T[] a) {
synchronized (mutex) {return c.toArray(a);}
}
public Iterator<E> iterator() {
return c.iterator(); // Must be manually synched by user!
}
public boolean add(E e) {
synchronized (mutex) {return c.add(e);}
}
public boolean remove(Object o) {
synchronized (mutex) {return c.remove(o);}
}
public boolean containsAll(Collection<?> coll) {
synchronized (mutex) {return c.containsAll(coll);}
}
public boolean addAll(Collection<? extends E> coll) {
synchronized (mutex) {return c.addAll(coll);}
}
public boolean removeAll(Collection<?> coll) {
synchronized (mutex) {return c.removeAll(coll);}
}
public boolean retainAll(Collection<?> coll) {
synchronized (mutex) {return c.retainAll(coll);}
}
public void clear() {
synchronized (mutex) {c.clear();}
}
public String toString() {
synchronized (mutex) {return c.toString();}
}
// Override default methods in Collection
@Override
public void forEach(Consumer<? super E> consumer) {
synchronized (mutex) {c.forEach(consumer);}
}
@Override
public boolean removeIf(Predicate<? super E> filter) {
synchronized (mutex) {return c.removeIf(filter);}
}
@Override
public Spliterator<E> spliterator() {
return c.spliterator(); // Must be manually synched by user!
}
@Override
public Stream<E> stream() {
return c.stream(); // Must be manually synched by user!
}
@Override
public Stream<E> parallelStream() {
return c.parallelStream(); // Must be manually synched by user!
}
private void writeObject(ObjectOutputStream s) throws IOException {
synchronized (mutex) {s.defaultWriteObject();}
}
}
根据文档Iterator是SynchronizedCollection未同步。因此,如果迭代一个线程,另一个线程可以添加/删除。所以ConcurrentModificationException将会出现。