Java 并发--- 同步容器、ConcurrentModificationException异常

同步容器

在Java中,同步容器主要包括2类:

  1. Vector、Stack、HashTable。Vector实现了List接口,Vector实际上就是一个数组,和ArrayList类似,但是Vector中的方法都是synchronized方法,即进行了同步措施。Stack也是一个同步容器,它的方法也用synchronized进行了同步,它实际上是继承于Vector类。HashTable实现了Map接口,它和HashMap很相似,但是HashTable进行了同步处理,而HashMap没有。
  2. Collections类中提供的静态工厂方法创建的类。
Collections.synchronizedCollection(Collection<T> c)
Collections.synchronizedSet(Set<T> s)
Collections.synchronizedList(List<T> list)
Collections.synchronizedMap(Map<K,V> m)

同步容器的缺陷:

  1. 从同步容器的具体实现源码可知,同步容器中的方法采用了synchronized进行了同步,这必然会影响到执行性能。
  2. 同步容器并不能保证所有操作都是现成安全的。
  3. 在对Vector等容器并发地进行迭代修改时,会报ConcurrentModificationException异常。

ConcurrentModificationException异常

对Vector、ArrayList在迭代的时候如果同时对其进行修改就会抛出java.util.ConcurrentModificationException异常。即:

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2));
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer == 2)
                list.remove(integer);
        }
    }
}

结果抛出异常:java.util.ConcurrentModificationException

同样

    for(Integer i: list){
        if(2 == i){
            list.remove(i);
        }
    }

也会抛出异常。

但是有一点,java7下,要修改的元素不是list的最后一位则不会抛异常,不懂。

    ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2,3));
    Iterator<Integer> iterator = list.iterator();
    while(iterator.hasNext()){
        Integer integer = iterator.next();
        if(integer == 2)
            list.remove(integer);
    }

上面这种情况不会抛出异常。

ConcurrentModificationException 异常原因

// TODO 分析源码

原因:调用list.remove()方法导致modCount和expectedModCount的值不一致。

单线程下解决办法

(1)调用Iterator.remove()

public class Test {
    public static void main(String[] args)  {
        ArrayList<Integer> list = new ArrayList<Integer>(Arrays.asList(1,2));
        Iterator<Integer> iterator = list.iterator();
        while(iterator.hasNext()){
            Integer integer = iterator.next();
            if(integer == 2)
                iterator.remove();   //注意这个地方
        }
    }
}

(2)额外使用list保存要删除的元素

    ArrayList<Integer> list = new ArrayList<>(Arrays.asList(1,2));
    Iterator<Integer> iterator = list.iterator();
    
    ArrayList<Integer> toRemove = new ArrayList<>();

    while (iterator.hasNext()){
        Integer integer = iterator.next();
        if(integer == 2){
            toRemove.add(integer);
        }
    }

    list.removeAll(toRemove);

多线程下解决办法

上述方法中的第一种,在多线程下并不适用。不管使用的是ArrayList还是线程安全的Vector,都可能会抛出异常。

通常的两种解决办法:

  1. 在使用iterator迭代的时候使用synchronized或者Lock进行同步;
  2. 使用并发容器CopyOnWriteArrayList代替ArrayList和Vector。
posted @ 2016-10-03 21:02  Hesier  阅读(837)  评论(0编辑  收藏  举报