Java 当从多个线程修改时,为什么ArrayList不抛出ConcurrentModificationException?

Java 当从多个线程修改时,为什么ArrayList不抛出ConcurrentModificationException?,java,multithreading,arraylist,thread-safety,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(

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();
}
final List tickets=new ArrayList(100000);
对于(int i=0;i<100000;i++){
票。添加(“票号,+i”);
}
对于(int i=0;i<10;i++){
Thread salethread=新线程(){
公开募捐{
while(tickets.size()>0){
票证。删除(0);
System.out.println(Thread.currentThread().getId()+“删除0”);
}
}
};
salethread.start();
}
代码很简单。 10个线程从arraylist对象中删除元素。 确保多个线程访问一个对象。但它运行正常。没有抛出异常。
为什么?

为了您的利益,我引用了Javadoc的一大部分。将突出显示解释您看到的行为的相关部分

请注意,此实现未同步。如果有多个线程 并发访问ArrayList实例,并至少访问 线程从结构上修改列表,它必须同步 外部。(A)结构修改是指添加或 删除一个或多个元素,或显式调整支持数组的大小; 仅仅设置一个元素的值并不是一个结构性的问题 这通常是通过在某些服务器上进行同步来实现的 对象,该对象自然地封装了列表。如果不存在这样的物体, 应使用Collections.synchronizedList“包装”列表 方法。最好在创建时执行此操作,以防止意外事件 对列表的非同步访问:

List List=Collections.synchronizedList(新的ArrayList(…)

此类的迭代器和listIterator方法返回的迭代器 是否快速失败:如果列表在之后的任何时间进行结构修改 迭代器是以任何方式创建的,除了通过迭代器自己的方式 如果删除或添加方法,迭代器将抛出 ConcurrentModificationException。因此,面对同时发生的冲突, 修改后,迭代器会快速、干净地失败,而不是 在不确定的时间冒任意、不确定行为的风险 将来

请注意,无法保证迭代器的快速失败行为 一般来说,不可能作出任何硬性保证 在存在非同步并发修改的情况下。快速失败 迭代器尽最大努力抛出ConcurrentModificationException 基础。因此,编写依赖于 关于其正确性的例外:的fail fast行为 迭代器只能用于检测bug

如果在通过迭代器访问列表时从结构上修改列表,ArrayList通常会引发并发修改异常(但即使这样也不是绝对的保证)。请注意,在您的示例中,您直接从列表中删除元素,而不是使用迭代器


如果您喜欢它,还可以浏览的实现,以更好地了解它的工作原理

您没有收到
ConcurrentModificationException
的原因是
ArrayList.remove
不会抛出异常。您可能可以通过启动一个额外的线程在数组中进行迭代来获得一个:

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();
}
new Thread() {
    public void run() {
        int totalLength = 0;
        for (String s : tickets) {
            totalLength += s.length();
        }
    }
}.start();
final List tickets=new ArrayList(100000);
对于(int i=0;i<100000;i++){
票。添加(“票号,+i”);
}
对于(int i=0;i<10;i++){
Thread salethread=新线程(){
公开募捐{
while(tickets.size()>0){
票证。删除(0);
System.out.println(Thread.currentThread().getId()+“删除0”);
}
}
};
salethread.start();
}
新线程(){
公开募捐{
int-totalength=0;
用于(字符串s:票){
总长度+=s.长度();
}
}
}.start();

我不认为“并发”在本例中是指线程相关的,或者至少它不一定意味着线程相关
ConcurrentModificationException
s通常是在迭代集合的过程中修改集合时产生的

List<String> list = new ArrayList<String>();
for(String s : list)
{
     //modifying list results in ConcurrentModificationException
     list.add("don't do this");     

}

因为您没有使用迭代器,所以不会抛出
ConcurrentModificationException

调用
remove(0)
只会删除第一个元素。如果另一个线程在执行完成之前删除0,则调用方可能不希望使用相同的元素

但它运行正常。没有抛出异常。为什么?

仅仅是因为并行修改是允许的

例外情况的描述如下:

“当不允许同时修改对象时,检测到该对象同时修改的方法可能会引发此异常。”

明确的含义是允许(或可能允许)同时修改。事实上,对于标准Java非并发集合类,允许并发修改。。。前提是它们不会在迭代过程中发生


这背后的原因是,对于非并发集合,迭代时的修改从根本上说是不安全和不可预测的。即使正确地同步(这并不容易),结果仍然是不可预测的。常规集合类中包含了并发修改的“快速失败”检查,因为这是使用Java1.1集合类的多线程应用程序中常见的海森堡

1-例如,“synchronizedXxx”包装类不同步,也不能同步
for(Iterator it = list.iterator(); it.hasNext())
{
     //no ConcurrentModificationException
     it.remove(); 
}