2

Vector是线程安全的,但在这个实现中它似乎不起作用。使方法同步也无济于事,java.util.ConcurrentModificationException被抛出。

可以做些什么来解决这个实现呢?

我应该去java.util.concurrent.CopyOnWriteArrayList吗?

import java.util.Iterator;
import java.util.Vector;

public class VectorExample {

    private Vector<Object> v;

    public  VectorExample() {    
        v = new Vector<Object>();
    }

    public  void  addToVector(Object o) {
        v.add(o);
    }

    public Vector<Object> getV() {
        return v;
    }

    public static void main(String[] args) {    
        final VectorExample v = new VectorExample();
        v.addToVector("obj1");
        v.addToVector("obj2");
        v.addToVector("obj3");
        v.addToVector("obj4");
        v.addToVector("obj5");
        v.addToVector("obj6");    

        new Thread(new Runnable() {
            @Override
            public  void run() {
                Iterator<Object> it = v.getV().iterator();

                while(it.hasNext()) {
                    System.out.println(it.next().toString());
                }    
            }
        }).start();

        new Thread(new Runnable() {    
            @Override
            public  void run() {
                v.addToVector("Obj11");
                v.addToVector("Obj12");
                v.addToVector("Obj13");
                v.addToVector("Obj14");
                v.addToVector("Obj15");
                v.addToVector("Obj16");
            }    
        }).start();    
    }
}
4

5 回答 5

2

看起来您遇到了异常,因为您同时迭代和修改集合。您可以尝试克隆集合并迭代克隆。

于 2013-04-22T18:23:48.107 回答
2

这个问题不能通过简单地向现有方法添加同步来解决,因为它涉及迭代器和变异的交叉使用。

Javadoc

通常不允许一个线程在另一个线程对其进行迭代时修改 Collection。一般来说,在这些情况下,迭代的结果是不确定的。如果检测到此行为,某些迭代器实现(包括 JRE 提供的所有通用集合实现的那些)可能会选择抛出此异常。这样做的迭代器被称为快速失败迭代器,因为它们快速而干净地失败,而不是在未来不确定的时间冒着任意的、非确定性的行为的风险。

推杆

 synchronized (v) { ... }

围绕第run()一种方法的主体并进行addToVector同步将通过确保迭代器的整个生命周期与向量的突变相互排斥来解决直接问题。

为了解决更大的问题,客户端需要知道在迭代器的整个生命周期内同步,您可能应该公开一个同步折叠方法而不是公开迭代器。这样,每个读者不需要同步他们的整个阅读。

interface Folder<IN, OUT> {
  OUT foldOne(OUT x, IN element);
}

<OUT>
synchronized OUT foldLeft(Folder<? super Object, OUT> folder, OUT x) {
  for (Object element : v) {
    x = folder.foldOne(x, element);
  }
  return x;
}

或者,如果迭代是不频繁的操作,只需将迭代器返回到向量的副本中。

于 2013-04-22T18:24:36.613 回答
2

一种可能性:在每个运行运行方法中放置同步块:

    synchronized ( v ) {
        Iterator<Object> it = v.getV().iterator();

        while(it.hasNext())
        {
            System.out.println(it.next().toString());
        }
    }

    synchronized ( v ) {
        v.addToVector("Obj11");
        v.addToVector("Obj12");
        v.addToVector("Obj13");
        v.addToVector("Obj14");
        v.addToVector("Obj15");
        v.addToVector("Obj16");
    }

另一个提示:

第一个运行方法的代码可以在 VectorExample 中,如 dump() 或其他。这将使同步更容易。

于 2013-04-22T18:25:44.780 回答
0

问题是您需要保护整个迭代,而不仅仅是get().

写入时,iterator来自Vector类的 变得无效。Vector如果之后使用它会抛出一个ConcurrentModificationException

有两种方法可以解决此问题。您可以同步包含循环的代码块和对同一个锁对象的 add 调用,或者您可以使用不同的集合,允许在输入新数据后对旧数据完成迭代。

于 2013-04-22T18:22:49.490 回答
0

迭代集合时不能修改集合。您可以制作集合的副本并迭代副本或同步集合上的两个块。

synchronized(v) {
  ..
}
于 2013-04-22T18:26:09.553 回答