`
lovnet
  • 浏览: 6882670 次
  • 性别: Icon_minigender_1
  • 来自: 武汉
文章分类
社区版块
存档分类
最新评论

java.util.ConcurrentModificationException

 
阅读更多

当方法检测到对象的并发修改,但不允许这种修改时,抛出此异常。

例如,某个线程在 Collection 上进行迭代时,通常不允许另一个线性修改该 Collection。通常在这些情况下,迭代的结果是不明确的。如果检测到这种行为,一些迭代器实现(包括 JRE 提供的所有通用 collection 实现)可能选择抛出此异常。执行该操作的迭代器称为快速失败 迭代器,因为迭代器很快就完全失败,而不会冒着在将来某个时间任意发生不确定行为的风险。

注意,此异常不会始终指出对象已经由不同 线程并发修改。如果单线程发出违反对象协定的方法调用序列,则该对象可能抛出此异常。例如,如果线程使用快速失败迭代器在 collection 上迭代时直接修改该 collection,则迭代器将抛出此异常。

注意,迭代器的快速失败行为无法得到保证,因为一般来说,不可能对是否出现不同步并发修改做出任何硬性保证。快速失败操作会尽最大努力抛出 ConcurrentModificationException。因此,为提高此类操作的正确性而编写一个依赖于此异常的程序是错误的做法,正确做法是:ConcurrentModificationException 应该仅用于检测 bug。


/*
 * @(#)ConcurrentModificationException.java	1.20 06/03/30
 *
 * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
 * SUN PROPRIETARY/CONFIDENTIAL. Use is subject to license terms.
 */

package java.util;

/**
 * This exception may be thrown by methods that have detected concurrent
 * modification of an object when such modification is not permissible.
 * <p>
 * For example, it is not generally permissible for one thread to modify a Collection
 * while another thread is iterating over it.  In general, the results of the
 * iteration are undefined under these circumstances.  Some Iterator
 * implementations (including those of all the general purpose collection implementations
 * provided by the JRE) may choose to throw this exception if this behavior is
 * detected.  Iterators that do this are known as <i>fail-fast</i> iterators,
 * as they fail quickly and cleanly, rather that risking arbitrary,
 * non-deterministic behavior at an undetermined time in the future.
 * <p>
 * Note that this exception does not always indicate that an object has
 * been concurrently modified by a <i>different</i> thread.  If a single
 * thread issues a sequence of method invocations that violates the
 * contract of an object, the object may throw this exception.  For
 * example, if a thread modifies a collection directly while it is
 * iterating over the collection with a fail-fast iterator, the iterator
 * will throw this exception.
 *
 * <p>Note that fail-fast behavior cannot be guaranteed as it is, generally
 * speaking, impossible to make any hard guarantees in the presence of
 * unsynchronized concurrent modification.  Fail-fast operations
 * throw <tt>ConcurrentModificationException</tt> on a best-effort basis. 
 * Therefore, it would be wrong to write a program that depended on this
 * exception for its correctness: <i><tt>ConcurrentModificationException</tt>
 * should be used only to detect bugs.</i>
 *
 * @author  Josh Bloch
 * @version 1.20, 03/30/06
 * @see	    Collection
 * @see     Iterator
 * @see     ListIterator
 * @see	    Vector
 * @see	    LinkedList
 * @see	    HashSet
 * @see	    Hashtable
 * @see	    TreeMap
 * @see	    AbstractList
 * @since   1.2
 */
public class ConcurrentModificationException extends RuntimeException {
    /**
     * Constructs a ConcurrentModificationException with no
     * detail message.
     */
    public ConcurrentModificationException() {
    }

    /**
     * Constructs a <tt>ConcurrentModificationException</tt> with the
     * specified detail message.
     *
     * @param message the detail message pertaining to this exception.
     */
    public ConcurrentModificationException(String message) {
	super(message);
    }
}


一般List报错的原因:

Iterator 是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出 java.util.ConcurrentModificationException 异常。
所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法 remove() 来删除对象, Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。


源码:

AbstractList

	public E next() {
            checkForComodification();
	    try {
		E next = get(cursor);
		lastRet = cursor++;
		return next;
	    } catch (IndexOutOfBoundsException e) {
		checkForComodification();
		throw new NoSuchElementException();
	    }
	}

......

	final void checkForComodification() {
	    if (modCount != expectedModCount)
		throw new ConcurrentModificationException();
	}

......

protected transient int modCount = 0;

.....

int expectedModCount = modCount;


    /**
     * The number of times this list has been <i>structurally modified</i>.
     * Structural modifications are those that change the size of the
     * list, or otherwise perturb it in such a fashion that iterations in
     * progress may yield incorrect results.
     *
     * <p>This field is used by the iterator and list iterator implementation
     * returned by the {@code iterator} and {@code listIterator} methods.
     * If the value of this field changes unexpectedly, the iterator (or list
     * iterator) will throw a {@code ConcurrentModificationException} in
     * response to the {@code next}, {@code remove}, {@code previous},
     * {@code set} or {@code add} operations.  This provides
     * <i>fail-fast</i> behavior, rather than non-deterministic behavior in
     * the face of concurrent modification during iteration.
     *
     * <p><b>Use of this field by subclasses is optional.</b> If a subclass
     * wishes to provide fail-fast iterators (and list iterators), then it
     * merely has to increment this field in its {@code add(int, E)} and
     * {@code remove(int)} methods (and any other methods that it overrides
     * that result in structural modifications to the list).  A single call to
     * {@code add(int, E)} or {@code remove(int)} must add no more than
     * one to this field, or the iterators (and list iterators) will throw
     * bogus {@code ConcurrentModificationExceptions}.  If an implementation
     * does not wish to provide fail-fast iterators, this field may be
     * ignored.
     */
    protected transient int modCount = 0;



下面是Iterator的remove为什么不报错的原因:

    /**
     * Base class for Iterators for LinkedBlockingDeque
     */
    private abstract class AbstractItr implements Iterator<E> {
        /**
         * The next node to return in next
         */
         Node<E> next;

        /**
         * nextItem holds on to item fields because once we claim that
         * an element exists in hasNext(), we must return item read
         * under lock (in advance()) even if it was in the process of
         * being removed when hasNext() was called.
         */
        E nextItem;

        /**
         * Node returned by most recent call to next. Needed by remove.
         * Reset to null if this element is deleted by a call to remove.
         */
        private Node<E> lastRet;

        AbstractItr() {
            advance(); // set to initial position
        }

        /**
         * Advances next, or if not yet initialized, sets to first node.
         * Implemented to move forward vs backward in the two subclasses.
         */
        abstract void advance();

        public boolean hasNext() {
            return next != null;
        }

        public E next() {
            if (next == null)
                throw new NoSuchElementException();
            lastRet = next;
            E x = nextItem;
            advance();
            return x;
        }

        public void remove() {
            Node<E> n = lastRet;
            if (n == null)
                throw new IllegalStateException();
            lastRet = null;
            // Note: removeNode rescans looking for this node to make
            // sure it was not already removed. Otherwise, trying to
            // re-remove could corrupt list.
            removeNode(n);
        }
    }

    /** Forward iterator */
    private class Itr extends AbstractItr {
        void advance() {
            final ReentrantLock lock = LinkedBlockingDeque.this.lock;
            lock.lock();
            try {
                next = (next == null)? first : next.next;
                nextItem = (next == null)? null : next.item;
            } finally {
                lock.unlock();
            }
        }
    }

.....

    /**
     * Variant of removeFirstOccurrence needed by iterator.remove.
     * Searches for the node, not its contents.
     */
    boolean removeNode(Node<E> e) {
        lock.lock();
        try {
            for (Node<E> p = first; p != null; p = p.next) {
                if (p == e) {
                    unlink(p);
                    return true;
                }
            }
            return false;
        } finally {
            lock.unlock();
        }
    }


分享到:
评论

相关推荐

    java.util.ConcurrentModificationException 异常问题详解1

    Java.util.ConcurrentModificationException 异常问题详解 ConcurrentModificationException 异常是 Java 中一个常见的异常,它发生在 Iterator 遍历集合时,集合同时被修改引起的异常。在 Java 中,集合类如 ...

    出现java.util.ConcurrentModificationException 问题及解决办法

    在Java编程中,`java.util.ConcurrentModificationException` 是一个常见的运行时异常,通常发生在尝试并发修改集合时。这个异常的产生是由于集合类(如HashMap)的非线程安全特性,当你在一个线程中使用迭代器遍历...

    java.util.ConcurrentModificationException 解决方法

    `java.util.ConcurrentModificationException` 是一个在 Java 中常见的运行时异常,它通常发生在多线程环境中,当一个线程正在遍历一个集合(如 `ArrayList`, `HashMap` 等),而另一个线程同时尝试修改这个集合时。...

    java.util.ConcurrentModificationException(解决方案).md

    常见问题

    Java语言的Util类详细介绍

    Java语言的Util类详细介绍 Java语言的Util类是Java开发中非常重要的一部分,它提供了一系列的类来实现基本的数据结构,如线性表、链表等。这些类均在java.util包中。 Collection接口是Java中最基本的集合接口,一...

    spring-data-mongodb-test:在Collections.synchronizedList或Collections.synchronizedSet上测试spring数据mongodb ConcurrentModificationException

    Spring数据mongodb测试 在Collections.synchronizedList或Collections.synchronizedSet上测试spring数据mongodb ConcurrentModificationException

    java8集合源码-zinc-ConcurrentModificationException:锌并发修改异常

    java.util.ConcurrentModificationException: mutation occurred during iteration [error] scala.collection.mutable.MutationTracker$.checkMutations(MutationTracker.scala:43) [error] scala.collection....

    JAVA.BUG模式详解

    使用`synchronized`关键字、`volatile`变量、`java.util.concurrent`包中的工具类等可以有效地管理并发。 四、内存泄漏 Java中的内存泄漏并不像C++那样直接导致资源耗尽,但过度持有对象引用会导致垃圾收集器无法...

    使用Iterator接口遍历集合元素

    否则将会引发 java.util.ConcurrentModificationException 异常。 3. Iterator 迭代器采用的是快速失败(fail-fast)机制,一旦在迭代过程中检测到该集合已经被修改(通常是程序中其它线程修改),程序立即引发 ...

    jdk 1.6 API 中文版帮助文档

    - `java.util.Iterator`的改进:支持`remove()`操作,避免抛出`ConcurrentModificationException`。 ### 4. 性能优化 JDK 1.6对编译器和垃圾收集器进行了优化,提高了运行效率,例如: - **Server VM的改进**: ...

    collecter集合总结

    需要注意的是,在迭代的过程中不能对集合中元素进行修改,否则将产生 java.util.ConcurrentModificationException。 选择集合 在实际开发中,选择合适的集合是非常重要的。以下是一些选择集合的建议: * 如果需要...

    迭代大师的修炼之道:Java中Iterator与增强for循环的深度解析

    import java.util.ArrayList; import java.util.Iterator; public class IteratorExample { public static void main(String[] args) { ArrayList&lt;String&gt; names = new ArrayList(); names.add("Ada Lovelace");...

    Java多线程安全集合

    Java的`java.util.concurrent`包提供了更为高效且专门设计用于并发操作的集合。比如: - `ConcurrentHashMap`:线程安全的哈希映射,比`synchronized Map`性能更好,因为它允许不同部分独立加锁,减少了锁竞争。 ...

    多线程中使用Java集合类.doc

    4. 如果需要更复杂的并发控制,可以使用`java.util.concurrent.locks`包下的Lock接口及其实现,如ReentrantLock,配合`tryLock()`方法进行细粒度的锁控制。 总的来说,处理多线程环境中的Java集合类时,开发者需要...

    Java中CopyOnWriteArrayList的使用

    java中,List在遍历的时候,如果被修改了会抛出java.util.ConcurrentModificationException错误。  看如下代码: import java.util.ArrayList; import java.util.List; public class Resource3 { public ...

    Java源码分析:深入探讨Iterator模式

    在Java编程语言中,集合框架(`java.util`包)提供了多种容器类来存储对象,如`List`、`Set`和`Map`等。为了遍历这些容器中的元素,Java引入了迭代器模式(Iterator Pattern),这是一种常用的设计模式,它提供了一...

Global site tag (gtag.js) - Google Analytics