Java ArrayList类中的数据竞争

时间:2010-10-01 05:09:00

标签: java multithreading

我正在阅读CopyOnWriteArrayList,并想知道如何在ArrayList课程中展示数据竞争。基本上我正在尝试模拟ArrayList失败的情况,因此有必要使用CopyOnWriteArrayList。关于如何模拟这个的任何建议。

3 个答案:

答案 0 :(得分:6)

竞争是指两个(或更多)线程尝试对共享数据进行操作,最终输出取决于访问数据的顺序(并且该顺序是不确定的)

来自维基百科:

  

竞争条件或种族危险是电子系统或过程中的缺陷,其中过程的输出和/或结果出乎意料地且严重地取决于其他事件的顺序或时间。该术语源于两个信号相互竞争以影响输出的想法。

例如:

public class Test  {
    private static List<String> list = new CopyOnWriteArrayList<String>();

    public static void main(String[] args) throws Exception {
        ExecutorService e = Executors.newFixedThreadPool(5);
        e.execute(new WriterTask());
        e.execute(new WriterTask());
        e.execute(new WriterTask());
        e.execute(new WriterTask());
        e.execute(new WriterTask());

        e.awaitTermination(20, TimeUnit.SECONDS);
    }

    static class WriterTask implements Runnable {

        @Override
        public void run() {
            for (int i = 0; i < 25000; i ++) {
                list.add("a");
            }
        }
    }
}

但是,ArrayListArrayIndexOutOfbounds一起使用时,这会失败。那是因为在插入之前应该调用ensureCapacity(..)以确保内部数组可以保存新数据。这就是发生的事情:

  • 第一个帖子调用add(..),后者又调用ensureCapacity(currentSize + 1)
  • 在第一个线程实际增加大小之前,第二个线程也调用ensureCapacity(currentSize + 1)
  • 因为两者都已读取currentSize的初始值,内部数组的新大小为currentSize + 1
  • 两个线程进行昂贵的操作,将旧数组复制到扩展数组中,使用新的大小(不能同时包含两个
  • 然后他们每个人都试图将新元素分配给array[size++]。第一个成功,第二个失败,因为内部阵列由于接收条件而没有正确扩展。

这种情况发生了,因为两个线程试图在同一个结构上同时添加项目,并且其中一个线程的添加覆盖了另一个线程的添加(即第一个丢失了)

CopyOnWriteArrayList

的另一个好处
  • 多个线程写入ArrayList
  • 一个线程迭代ArrayList。肯定会得到ConcurrentModificationException

以下是如何演示它:

public class Test  {
    private static List<String> list = new ArrayList<String>();

    public static void main(String[] args) throws Exception {
        ExecutorService e = Executors.newFixedThreadPool(2);
        e.execute(new WriterTask());
        e.execute(new ReaderTask());
    }

    static class ReaderTask implements Runnable {
        @Override
        public void run() {
            while (true) {
                for (String s : list) {
                    System.out.println(s);
                }
            }
        }
    }

    static class WriterTask implements Runnable {
        @Override
        public void run() {
            while(true) {
                list.add("a");
            }
        }
    }
}

如果你多次运行这个程序,那么在获得ConcurrentModificationException之前,你常常会得到OutOfMemoryError

如果用CopyOnWriteArrayList替换它,则不会出现异常(但程序非常慢)

请注意,这只是一个演示 - CopyOnWriteArrayList的好处是当读取次数大大超过写入次数时。

答案 1 :(得分:0)

示例:

for (int i = 0; i < array.size(); ++i) {
    Element elm = array.get(i);
    doSomethingWith(elm);
}

如果另一个线程在此线程调用array.get(i)之前调用array.clear(),但在将i与array.size(), - &gt;进行比较之后ArrayIndexOutOfBoundsException异常。

答案 2 :(得分:-2)

两个线程,一个递增arraylist,一个递减。数据竞争可能发生在这里。