黑马程序员技术交流社区

标题: Vector 是同步的,但是为什么修改集合元素还是抛出异常 [打印本页]

作者: 黑马刘涛    时间: 2012-7-14 19:26
标题: Vector 是同步的,但是为什么修改集合元素还是抛出异常
本帖最后由 黑马刘涛 于 2012-7-16 12:13 编辑
  1. import java.util.*;
  2. class Student{
  3.     public Student(String name, int age){
  4.         this.name = name;
  5.         this.age = age;        
  6.     }
  7.     String name;
  8.     int age;
  9.    
  10.     public String  toString(){
  11.         return "I am "+name+" , "+age+" years old.";
  12.     }
  13. }
  14. /*创建一个修改Collection的线程,实现Runnable接口。不采取任何同步措施。*/
  15. class ModifyCollectionTask implements Runnable{   
  16.     public ModifyCollectionTask(Collection<Student> slist){
  17.         this.slist = slist;
  18.     }
  19.     public void run(){        
  20.         // 遍历学生列表,
  21.         for(Student s : slist){
  22.             System.out.println(Thread.currentThread().getName());
  23.             System.out.println(s);
  24.         }
  25.         // 向学生列表添加元素
  26.         slist.add(new Student("Katie", 30));        
  27.     }
  28.     Collection<Student> slist;
  29. }
  30. class ModifyTest
  31. {
  32.         public static void main(String[] args)
  33.         {      /*由于Vector类是线程安全的动态数组,所以,将集合实现改为Vector*/
  34.                 // 使用Vector
  35.                 List<Student> sVector = new Vector<Student>();
  36.                 sVector.add(new Student("AAA",10));
  37.                 sVector.add(new Student("BBB",12));
  38.                 sVector.add(new Student("CCC",14));
  39.                 sVector.add(new Student("DDD",16));
  40.                 sVector.add(new Student("EEE",18));
  41.                
  42.                 for(int i=0;i<3;i++){
  43.                         new Thread(new ModifyCollectionTask(sVector)).start();
  44.                 }
  45.         }
  46. }
  47. //为什么还是会抛出ConcurrentModificationException异常。既然迭代器抛出这个异常,肯定是迭代器的问题,我没动迭代器啊。
复制代码

作者: 温少邦    时间: 2012-7-14 20:04
Vector是线程同步的
可以有几个线程同时对Vector进行添加删除
但是使用迭代器迭代时还是不能对集合进行操作
如果要想在迭代时操作集合元素
可以使用Java5的CopyOnWriteArrayList
作者: 陈淑飞    时间: 2012-7-14 20:35
lZ,虽然Vector是线程安全的,但是楼主,你是开了多个线程,对同一Vertor,
在多线程run 代码中,同时进行add与迭代 存在的安全 隐换呢。 因为最终肯定有一线程先跑完
for(Student s : slist){
            System.out.println(Thread.currentThread().getName());
            System.out.println(s);
        }


时,他会执行到
slist.add(new Student("Katie", 30));  

,根据多态,他会调用 Vector的 add方法,
我们来看看 Vector 的add方法做了什么:
/**
     * Appends the specified element to the end of this Vector.
     *
     * @param e element to be appended to this Vector
     * @return {@code true} (as specified by {@link Collection#add})
     * @since 1.2
     */
    public synchronized boolean add(E e) {
        modCount++;
        ensureCapacityHelper(elementCount + 1);
        elementData[elementCount++] = e;
        return true;
    }


特别要注意上面的 modCount++; ,这个很重要。
接着,另一个线程在还在跑for 迭代时,肯定会执行迭代中的next()方法,
那么我们来看看,这个next 做了什么吧

public E next() {
            synchronized (Vector.this) {
                checkForComodification();
                int i = cursor;
                if (i >= elementCount)
                    throw new NoSuchElementException();
                cursor = i + 1;
                return elementData(lastRet = i);
            }
        }


从面上中看到 调用了 checkForComodification(); 进行了,同步的检查。
我们再来看看这个方法:


private class Itr implements Iterator<E> {
        int cursor;       // index of next element to return
        int lastRet = -1; // index of last element returned; -1 if no such
        int expectedModCount = modCount;

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


此时,我们可以明显的确认:
对 add后,由于modCount++了,但是 expectedModCount却没有变。
所以,modCount != expectedModCount 为true了, 此时就抛出来上面的异常了。

实际上,不至在迭代时,不能用add方法,remove方法也是不能的。这两个方法中
modCount++ 都会自加,而expectedModCount 却无变化呢

作者: 黑马刘涛    时间: 2012-7-14 21:59
现在提问都没分了。
作者: 黑马刘涛    时间: 2012-7-14 22:11
陈淑飞 发表于 2012-7-14 20:35
lZ,虽然Vector是线程安全的,但是楼主,你是开了多个线程,对同一Vertor,
在多线程run 代码中,同时进行a ...

感谢回答,确实foreach语句在编译时编译器会展开为迭代器迭代,而由 Vector 的 iterator 和 listIterator 方法所返回的迭代器是快速失败的:如果在迭代器创建后的任意时间从结构上修改了向量(通过迭代器自身的 remove 或 add 方法之外的任何其他方式),则迭代器将抛出 ConcurrentModificationException。我是忽略了这个foreach语句的欺骗性了
作者: 周刚    时间: 2012-7-14 22:28
黑马刘涛 发表于 2012-7-14 21:59
现在提问都没分了。

我为你们这些提问了,却没有得分的人发了一个建议贴
作者: 黑马刘涛    时间: 2012-7-14 22:31
周刚 发表于 2012-7-14 22:28
我为你们这些提问了,却没有得分的人发了一个建议贴

是啊,这样一个问题我自己也是思考了很久解决不了才提问的。而且对其他人也有借鉴意义。




欢迎光临 黑马程序员技术交流社区 (http://bbs.itheima.com/) 黑马程序员IT技术论坛 X3.2