为什么ArrayList在从多个线程修改时不会抛出ConcurrentModificationException?

roa*_*oul 3 java multithreading arraylist thread-safety

ConcurrentModificationException:当不允许此类修改时,检测到并发修改对象的方法可能抛出此异常.

以上是来自javadoc的ConcurrentModificationException定义.

所以我尝试测试下面的代码:

final List<String> tickets = new ArrayList<String>(100000);
for (int i = 0; i < 100000; i++) {
    tickets.add("ticket NO," + i);
}
for (int i = 0; i < 10; i++) {
    Thread salethread = new Thread() {
        public void run() {
            while (tickets.size() > 0) {
                tickets.remove(0);
                System.out.println(Thread.currentThread().getId()+"Remove 0");
            }
        }
    };
    salethread.start();
}
Run Code Online (Sandbox Code Playgroud)

代码很简单.10个线程从arraylist对象中删除元素.确保多个线程访问一个对象.但它运行正常.没有异常被抛出.为什么?

Per*_*ion 7

ArrayList为了您的利益,我引用了Javadoc 的大部分内容.将突出显示解释您所看到的行为的相关部分.

请注意,此实现不同步.如果多个线程同时访问一个ArrayList实例,以及线程中的至少一个结构上修改了列表,它必须保持外部同步.(结构修改是添加或删除一个或多个元素的任何操作,或显式调整后备数组的大小;仅设置元素的值不是结构修改.)这通常通过同步一些自然封装的对象来实现.名单.如果不存在此类对象,则应使用Collections.synchronizedList方法"包装"该列表.这最好在创建时完成,以防止意外地不同步访问列表:

List list = Collections.synchronizedList(new ArrayList(...));

此类的iterator和listIterator方法返回的迭代器是快速失败的:如果在创建迭代器之后的任何时候对列表进行结构修改,除了通过迭代器自己的remove或add方法之外,迭代器将抛出ConcurrentModificationException.因此,在并发修改的情况下,迭代器快速而干净地失败,而不是在未来的未确定时间冒任意,非确定性行为的风险.

请注意,迭代器的故障快速行为无法得到保证,因为一般来说,在存在不同步的并发修改时,不可能做出任何硬性保证.失败快速迭代器会尽最大努力抛出ConcurrentModificationException.因此,编写依赖于此异常的程序以确保其正确性是错误的:迭代器的快速失败行为应该仅用于检测错误.

如果在通过迭代器访问列表时从结构上修改列表,ArrayLists通常会抛出并发修改异常(但即使这不是绝对的保证).请注意,在您的示例中,您将直接从列表中删除元素,并且您没有使用迭代器.

如果它引起你的兴趣,你也可以浏览它的实现ArrayList.remove,以便更好地理解它的工作原理.