zoukankan      html  css  js  c++  java
  • Java并发(五):并发,迭代器和容器

      在随后的博文中我会继续分析并发包源码,在这里,得分别谈谈容器类和迭代器及其源码,虽然很突兀,但我认为这对于学习Java并发很重要;

    ConcurrentModificationException:

      JavaAPI中的解释:当不允许这样的修改时,可以通过检测到对象的并发修改的方法来抛出此异常。一个线程通常不允许修改集合,而另一个线程正在遍历它。 一般来说,在这种情况下,迭代的结果是未定义的。 某些迭代器实现(包括由JRE提供的所有通用集合实现的实现)可能会选择在检测到此行为时抛出此异常。 这样做的迭代器被称为"及时失败"迭代器,当他们发现容器在迭代时被修改时,就会报异常;它称不上时一种处理机制,而是一种预防机制,只能作为并发问题的预警指示器.

    迭代器与容器:

      Vector这个"古老"的容器类相信大家很熟悉了,他是线程安全的没错,我们利用elements()方法遍历,不会出现线程安全的问题:

     1 /**
     2   *  JDK1.8源码
     3 */
     4 
     5 /**
     6      * Returns an enumeration of the components of this vector. The
     7      * returned {@code Enumeration} object will generate all items in
     8      * this vector. The first item generated is the item at index {@code 0},
     9      * then the item at index {@code 1}, and so on.
    10      *
    11      * @return  an enumeration of the components of this vector
    12      * @see     Iterator
    13      */
    14     public Enumeration<E> elements() {
    15         return new Enumeration<E>() {
    16             int count = 0;
    17 
    18             public boolean hasMoreElements() {
    19                 return count < elementCount;
    20             }
    21 
    22             public E nextElement() {
    23                 synchronized (Vector.this) {
    24                     if (count < elementCount) {
    25                         return elementData(count++);
    26                     }
    27                 }
    28                 throw new NoSuchElementException("Vector Enumeration");
    29             }
    30         };
    31     }
    JDK1.8源码: AbstractCollection (been extends by Vector)

      但当我们利用foreach进行迭代时,底层自动调用了iterator(),若我们不锁住容器,可能会报ConcurrentModificationException

     1 /**
     2      * Returns an iterator over the elements in this list in proper sequence.
     3      *
     4      * <p>The returned iterator is <a href="#fail-fast"><i>fail-fast</i></a>.
     5      *
     6      * @return an iterator over the elements in this list in proper sequence
     7      */
     8     public Iterator<E> iterator() {
     9         return new Itr();
    10         //JDK1.8写成了一个内部类的形式返回迭代器
    11     }
    12 
    13     /**
    14      * An optimized version of AbstractList.Itr
    15      */
    16     private class Itr implements Iterator<E> {
    17         int cursor;       // index of next element to return
    18         int lastRet = -1; // index of last element returned; -1 if no such
    19         int expectedModCount = modCount;
    20 
    21         Itr() {}
    22 
    23         public boolean hasNext() {
    24             return cursor != size;
    25         }
    26 
    27         //观察下面的代码可以发现,每次迭代一次都要检查容器长度是否改变
    28         @SuppressWarnings("unchecked")
    29         public E next() {
    30             checkForComodification();
    31             int i = cursor;
    32             if (i >= size)
    33                 throw new NoSuchElementException();
    34             Object[] elementData = ArrayList.this.elementData;
    35             if (i >= elementData.length)
    36                 throw new ConcurrentModificationException();
    37             cursor = i + 1;
    38             return (E) elementData[lastRet = i];
    39         }
    40 
    41         public void remove() {
    42             if (lastRet < 0)
    43                 throw new IllegalStateException();
    44             checkForComodification();
    45 
    46             try {
    47                 ArrayList.this.remove(lastRet);
    48                 cursor = lastRet;
    49                 lastRet = -1;
    50                 expectedModCount = modCount;
    51             } catch (IndexOutOfBoundsException ex) {
    52                 throw new ConcurrentModificationException();
    53             }
    54         }
    55 
    56         @Override
    57         @SuppressWarnings("unchecked")
    58         public void forEachRemaining(Consumer<? super E> consumer) {
    59             Objects.requireNonNull(consumer);
    60             final int size = ArrayList.this.size;
    61             int i = cursor;
    62             if (i >= size) {
    63                 return;
    64             }
    65             final Object[] elementData = ArrayList.this.elementData;
    66             if (i >= elementData.length) {
    67                 throw new ConcurrentModificationException();
    68             }
    69             while (i != size && modCount == expectedModCount) {
    70                 consumer.accept((E) elementData[i++]);
    71             }
    72             // update once at end of iteration to reduce heap write traffic
    73             cursor = i;
    74             lastRet = i - 1;
    75             checkForComodification();
    76         }
    77 
    78         final void checkForComodification() {
    79             if (modCount != expectedModCount)
    80                 throw new ConcurrentModificationException();
    81         }
    82     }    
    JDK1.8源码: Vector

       查看代码时可以发现,iterator()的内部类中提供的next,remove等方法都进行了迭代操作,这样的迭代被称为"隐藏迭代器";

      部分容器类的toString()方法会在调用StringBuilder的append()同时迭代容器,例如继承了AbstractSet的HashSet类,而AbstractSet又继承于AbstractCollection:

    //  String conversion
    
        /**
         * Returns a string representation of this collection.  The string
         * representation consists of a list of the collection's elements in the
         * order they are returned by its iterator, enclosed in square brackets
         * (<tt>"[]"</tt>).  Adjacent elements are separated by the characters
         * <tt>", "</tt> (comma and space).  Elements are converted to strings as
         * by {@link String#valueOf(Object)}.
         *
         * @return a string representation of this collection
         */
        public String toString() {
            Iterator<E> it = iterator();
            if (! it.hasNext())
                return "[]";
    
            StringBuilder sb = new StringBuilder();
            sb.append('[');
            for (;;) {
                E e = it.next();
                sb.append(e == this ? "(this Collection)" : e);
                if (! it.hasNext())
                    return sb.append(']').toString();
                sb.append(',').append(' ');
            }
        }
    JDK1.8源码: AbstractCollection

      隐藏的迭代器普遍存在,甚至出现在hashCode,equals,contains,remove等方法中,此处不再赘述;

    解决办法:

      考虑到并发安全性,我们不得不对容器类单独加锁,但同时我们又不希望加锁,那样太损耗性能了,还存在线程饥饿和死锁的风险,极大降低吞吐量和CPU利用率;

      作为有限的替代,我们可以考虑"克隆"容器,并在将其副本封闭起来进行迭代,避免了抛出ConcurrentModificationException,但在克隆过程中仍然要对容器加锁;显然,克隆容器存在系统开销,我们在选择这种方案时必须考虑的因素有:容器大小,每个元素上执行的工作,迭代操作相对于其它操作的调用频率,以及相应时间和系统吞吐率的需求,具体应用详见CopyOnWriteArrayList类.

    参考材料:

      Java并发编程实战

  • 相关阅读:
    HDU 1863 畅通工程(Kruskal)
    HDU 1879 继续畅通工程(Kruskra)
    HDU 1102 Constructing Roads(Kruskal)
    POJ 3150 Cellular Automaton(矩阵快速幂)
    POJ 3070 Fibonacci(矩阵快速幂)
    ZOJ 1648 Circuit Board(计算几何)
    ZOJ 3498 Javabeans
    ZOJ 3490 String Successor(模拟)
    Java实现 LeetCode 749 隔离病毒(DFS嵌套)
    Java实现 LeetCode 749 隔离病毒(DFS嵌套)
  • 原文地址:https://www.cnblogs.com/Joey44/p/10347732.html
Copyright © 2011-2022 走看看