Java ArrayList类中的数据竞争

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

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

来自维基百科:

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

例如:

 public class Test { private static List list = new CopyOnWriteArrayList(); 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++] 。 第一个成功,第二个失败,因为内部arrays由于接收条件而没有正确扩展。

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

CopyOnWriteArrayList另一个好处

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

以下是如何演示它:

 public class Test { private static List list = new ArrayList(); 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"); } } } } 

如果多次运行此程序, 获取OutOfMemoryError 之前 ,通常会收到ConcurrentModificationException

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

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

例:

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

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

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