此图是verctor容器产生并发的一个说明



虽然加锁可以防止迭代器抛出 concurentModicationException,但你必须记住对所有共享容器进行迭代的地方

都需要进行加锁。



经常在迭代集合元素时,会想对集合做修改(add/remove)操作,类似下面这段代码:

[java] view plaincopy

for (Iterator<Integer> it = list.iterator(); it.hasNext(); ) {  
    Integer val = it.next();  
    if (val == 5) {  
        list.remove(val);  
    }  
}

 


运行这段代码,会抛出异常java.util.ConcurrentModificationException。


【解惑】

(以ArrayList来讲解)在ArrayList中,它的修改操作(add/remove)都会对modCount这个字段+1,modCount可以看作一个版本号,每次集合中的元素被修改后,都会+1(即使溢出)。接下来再看看AbsrtactList中iteraor方法

[java] view plaincopy

public Iterator<E> iterator() {  
    return new Itr();  
}

它返回一个内部类,这个类实现了iterator接口,代码如下:


[java] view plaincopy

private class Itr implements Iterator<E> {  
    int cursor = 0;  
  
    int lastRet = -1;  
  
    int expectedModCount = modCount;  
  
    public boolean hasNext() {  
        return cursor != size();  
    }  
  
    public E next() {  
        checkForComodification();  
        try {  
            E next = get(cursor);  
            lastRet = cursor++;  
            return next;  
        } catch (IndexOutOfBoundsException e) {  
            checkForComodification();  
            throw new NoSuchElementException();  
        }  
    }  
  
    public void remove() {  
        if (lastRet == -1)  
            throw new IllegalStateException();  
        checkForComodification();  
  
        try {  
            AbstractList.this.remove(lastRet);  
            if (lastRet < cursor)  
                cursor--;  
            lastRet = -1;  
            // 修改expectedModCount 的值  
            expectedModCount = modCount;  
            } catch (IndexOutOfBoundsException e) {  
            throw new ConcurrentModificationException();  
        }  
    }  
  
    final void checkForComodification() {  
        if (modCount != expectedModCount)  
            throw new ConcurrentModificationException();  
    }  
    }


在内部类Itr中,有一个字段expectedModCount ,初始化时等于modCount,即当我们调用list.iterator()返回迭代器时,该字段被初始化为等于modCount。在类Itr中next/remove方法都有调用checkForComodification()方法,在该方法中检测modCount == expectedModCount,如果不相当则抛出异常ConcurrentModificationException。


前面说过,在集合的修改操作(add/remove)中,都对modCount进行了+1。

在看看刚开始提出的那段代码,在迭代过程中,执行list.remove(val),使得modCount+1,当下一次循环时,执行 it.next(),checkForComodification方法发现modCount != expectedModCount,则抛出异常。


【解决办法】

如果想要在迭代的过程中,执行删除元素操作怎么办?

再来看看内部类Itr的remove()方法,在删除元素后,有这么一句expectedModCount = modCount,同步修改expectedModCount 的值。所以,如果需要在使用迭代器迭代时,删除元素,可以使用迭代器提供的remove方法。对于add操作,则在整个迭代器迭代过程中是不允许的。 其他集合(Map/Set)使用迭代器迭代也是一样。




并发容器:

1:同步容器是将所有对容器状态的访问都串行化,以实现他们的线程安全性,这种方法的代价是严重降低并发性,当多个线程竞争容器的锁时,吞吐量将严重降低

并发容器是针对多线程并发访问设计的

2:concurrentHashMap://用来代替同步,且基于散列的HashMap

3:CopyOnWriteArrayList用于遍历操作为主要操作的情况下代替同步的List,

concurrentHashMap增加了一些常见的复合操作。若没有则添加,替换,以及有条件删除



queue和blockingQueue//队列

queue上的操作不会阻塞,如果队列为空那么获取那么获取元素的操作将返回空值,事实上正是通过linkedList来实现queue,但还需要一个queue类因为其能去掉linkedList的随机访问


blockQueue增加了阻塞的操作,如果队列为空那么获取元素的操作将阻塞,直到出现一个可用的元素,如果队列已满,那么插入操作将会阻塞直到队列中出现可用的空间,

生产者和消费者模式就是典型的使用场景。

Java 6也引用了

concurrentskipList concurrentSkipMap  分别作为sortMap和sortSet的替代品


例如用synchronizedMap包装TreeMap和treeSet


concurrentHashmap也是一个基于散列的hashMap但是其内部 使用粒度更细的加锁机制,即加锁机制就是使用分段锁