java—当多个线程同时使用迭代器在同一个arraylist上工作时,为什么这段代码不抛出concurrentmodificationexception

vaj7vani  于 2021-06-30  发布在  Java
关注(0)|答案(2)|浏览(376)

这里有两个线程在同一个arraylist上工作,一个线程读取元素,另一个线程删除特定的元素。我希望这个能扔出去 ConcurrentModificationException . 但它不扔为什么?

import java.util.ArrayList;
import java.util.ConcurrentModificationException;
import java.util.Iterator;

public class IteratorStudies {

    public static final ArrayList<String> arr ;

    static{

        arr = new ArrayList<>();

        for(int i=0;i<100;i++) {
            arr.add("someCommonValue");
        }
        arr.add("someSpecialValue");
    }

    private static Integer initialValue = 4;

    public static void main(String x[]) {

     Thread t1 = new Thread(){
          @Override
          public void start(){
              Iterator<String> arrIter = arr.iterator();
              while(arrIter.hasNext()){
                  try {
                      String str = arrIter.next();
                      System.out.println("value :" + str);
                  }catch(ConcurrentModificationException e){
                      e.printStackTrace();
                  }
              }
                System.out.println("t1 complete:"+arr);

          }
      };

        Thread t2 = new Thread(){
            @Override
            public void start(){
                Iterator<String> arrIter = arr.iterator();
                while(arrIter.hasNext()){
                    String str = arrIter.next();
                    if(str.equals("someSpecialValue")){
                        arrIter.remove();
                    }
                }

                System.out.println("t2 complete:"+arr);

            }
        };

        t2.start();
        t1.start();
    }

}
snz8szmq

snz8szmq1#

您已覆盖 start 两个线程示例的方法,而不是 run ,并且这些方法在主执行线程内完成,因此,不会同时执行线程,也不会 ConcurrentModificationThreadException 能在这里发生。

xkftehaa

xkftehaa2#

你犯了两个比较常见的错误。

concurrentmodificationexception与并发无关

你可能会认为,鉴于这个名字,comodex是关于并发性的。不是的。在中,您不需要线程来获取它。在这里,这个琐碎的代码将抛出它:

void example() {
    var list = new ArrayList<String>();
    list.add("a");
    list.add("b");
    for (String elem : list) {
        if (elem.equals("a")) list.remove(elem);
    }
}

这是因为Ecomodex是由迭代器抛出的,只是表示发生了这种情况:
有人做了迭代器。
有人以某种方式更改了列表(而不是通过迭代器的.remove()方法)
有人在#1中生成的迭代器上运行任何相关方法
因此,在上面的例子中,foreach循环隐式地生成一个迭代器(#1),然后 list.remove 方法被调用(#2),然后通过再次命中foreach循环,我们调用该迭代器上的相关方法( .hasNext() ),然后,瞧,comodex出现了。
事实上,多线程是不太可能的:毕竟,您应该假设,如果您与来自多个线程的某个对象进行交互,那么它将被破坏,因为该行为是未指定的,因此,您有一个bug,更糟糕的是,一个很难测试的bug。如果您在迭代时从另一个线程修改了一个普通的jane arraylist,就不能保证有一个comodex。你会得到的。你可能不会。电脑可能会从table上走下来,在百老汇碰碰运气。”“不特定的行为”是一个很好的表达方式:“不要,说真的。它会一直伤害你,因为你不能测试它;这将在你开发它的整个过程中都很好地工作,而且当你把这个重要的演示给大的wig客户时,它将以令人尴尬的方式在你身上失败。
从多个线程与一个对象进行交互的方法非常小心:检查特定对象的文档,明确说明发生了什么(即使用来自 java.util.concurrent 专门设计了“从多个线程与之交互”用例的包,如果没有,则使用锁定。这些都是很棘手的事情,所以在java中执行多线程的通常方法是首先不要共享状态。尽可能地隔离、反转控制,并使用具有内置事务内部函数的消息传递策略,例如消息队列(rabbitmq和friends)和数据库(具有事务)。

如何使用线程

你覆盖了 run() 方法,然后通过调用 start 方法。或者更好的是,不要重写run,在创建线程示例时传递一个可运行的示例。
你就是这么用线的。您没有-您重写了start,这意味着启动这些线程根本不会生成一个新线程,它只是在您的线程中运行负载。这就解释了你的具体情况,但是你试图做的(通过弄乱另一个线程的列表来见证comodex)也不会得到一个comodex——它会得到你未指定的行为,这意味着任何事情都会发生。

相关问题