Java和同步链表

时间:2014-12-14 22:46:44

标签: java multithreading collections synchronization

我有疑问。第一个是为什么当我们运行这个函数时,我们有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个或更多增强的循环?例如,我必须使用线程,并且在某个时候第一个从集合中删除元素,并且在某个时候第二个元素在集合中添加元素。即使在使用迭代器(使用迭代器增强的东西)的时候也应该是线程保存的东西 提前谢谢。

3 个答案:

答案 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将会出现。