多线程下抛出的ConcurrentModificationException异常原因分析

今天在使用多线程完成坦克大战的java小游戏时遇到了ConcurrentModificationException异常抛出的情况,通过追随源码发现是Vector迭代过程中出现了问题

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;

        public boolean hasNext() {
            // Racy but within spec, since modifications are checked
            // within or after synchronization in next/previous
            return cursor != elementCount;
        }

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

        public void remove() {
            if (lastRet == -1)
                throw new IllegalStateException();
            synchronized (Vector.this) {
                checkForComodification();
                Vector.this.remove(lastRet);
                expectedModCount = modCount;
            }
            cursor = lastRet;
            lastRet = -1;
        }

        @Override
        public void forEachRemaining(Consumer<? super E> action) {
            Objects.requireNonNull(action);
            synchronized (Vector.this) {
                final int size = elementCount;
                int i = cursor;
                if (i >= size) {
                    return;
                }
        @SuppressWarnings("unchecked")
                final E[] elementData = (E[]) Vector.this.elementData;
                if (i >= elementData.length) {
                    throw new ConcurrentModificationException();
                }
                while (i != size && modCount == expectedModCount) {
                    action.accept(elementData[i++]);
                }
                // update once at end of iteration to reduce heap write traffic
                cursor = i;
                lastRet = i - 1;
                checkForComodification();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
这是Vector源码中的一个内部类,这个内部类作为迭代器,当我们调用Vector的iterator方法时会返回一个该类对象
public synchronized Iterator<E> iterator() {
        return new Itr();
    }

通过查看报错信息了解到是在Iter这个类中的next()方法处中调用checkForComodification();

抛出的异常,也就是说我们在迭代的过程中调用next()后再调用checkForComodification()出现了问题

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

发现抛出异常的原因是modCount != expectedModCount判定结果为真,

我们跳转到modCount看看它代表的是什么,发现这个变量是Vector继承自AbstractList类中的一个属性,下面是这个属性的部分介绍:

The number of times this list has been structurally modified. 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.

那么我们便可以得知modeCount在这里代表了Vector在结构上的修改次数,也就是新增元素和删除元素这一类操作的次数,而这里的 expectedModCount就是Vector理应被修改的次数。所以就是因为实际对Vector修改的次数不等于理论上Vector被修改的次数从而造成了抛异常。通过查看源码发现,这个expectedModCount是Itr,也就是Vector的迭代器的一个类属性成员,

int expectedModCount = modCount;

默认给的初值就是modCount,并且查看源码发现expectedModCount是没有在其他任何地方出现可能被修改的操作,也就是说当我们通过Vector类对象调用iterator()方法时,它返回来的迭代器对象中的expectedModCount属性就已经固定,所以只有可能是因为在迭代的过程中modeCount发生了改变才会造成modCount != expectedModCount。

这时候我就想起来这是因为我在一个线程迭代一个Vector对象时,另一个线程在向该Vector对象中add元素,造成了modCount值的改变。我将问题代码抽象出来差不多就是下面的意思:

public class Thread08 {
    public static void main(String[] args) throws InterruptedException {
        Vector<Integer> integers = new Vector<>();
        Test test = new Test(integers);
        new Thread(test).start();
        int num=0;
        while(true){
            num++;
            integers.add(num);//向Vector对象中不断添加元素

            Thread.sleep(1000);
        }
    }
}
class Test implements Runnable{
    public Vector<Integer>x;

    public Test(Vector<Integer> x) {
        this.x = x;
    }

    @Override
    public void run() {//不停地去迭代同一个Vector
        while(true){
            Iterator<Integer> iterator = x.iterator();
            while (iterator.hasNext()) {
                Object next =  iterator.next();
                System.out.println(next);
            }
        }
    }
}

这段程序启动后,运行一段时间就会因为上面所说的原因抛异常。

解决方式也很简单,让两个线程在要操作该Vector对象的地方指定这个Vector对象为对象锁即可

public class Thread08 {
    public static void main(String[] args) throws InterruptedException {
        Vector<Integer> integers = new Vector<>();
        Test test = new Test(integers);
        new Thread(test).start();
        int num=0;
        while(true){
            num++;
            synchronized (integers){
                integers.add(num);//向Vector对象中不断添加元素
            }

            Thread.sleep(1000);
        }
    }
}
class Test implements Runnable{
    public Vector<Integer>x;

    public Test(Vector<Integer> x) {
        this.x = x;
    }

    @Override
    public void run() {//不停地去迭代同一个Vector
        while(true){
            synchronized (x){
                Iterator<Integer> iterator = x.iterator();
                while (iterator.hasNext()) {
                    Object next =  iterator.next();
                    System.out.println(next);
                }
            }
        }
    }
}

  • 3
    点赞
  • 8
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值