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

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

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

  1. import java.util.ArrayList;
  2. import java.util.ConcurrentModificationException;
  3. import java.util.Iterator;
  4. public class IteratorStudies {
  5. public static final ArrayList<String> arr ;
  6. static{
  7. arr = new ArrayList<>();
  8. for(int i=0;i<100;i++) {
  9. arr.add("someCommonValue");
  10. }
  11. arr.add("someSpecialValue");
  12. }
  13. private static Integer initialValue = 4;
  14. public static void main(String x[]) {
  15. Thread t1 = new Thread(){
  16. @Override
  17. public void start(){
  18. Iterator<String> arrIter = arr.iterator();
  19. while(arrIter.hasNext()){
  20. try {
  21. String str = arrIter.next();
  22. System.out.println("value :" + str);
  23. }catch(ConcurrentModificationException e){
  24. e.printStackTrace();
  25. }
  26. }
  27. System.out.println("t1 complete:"+arr);
  28. }
  29. };
  30. Thread t2 = new Thread(){
  31. @Override
  32. public void start(){
  33. Iterator<String> arrIter = arr.iterator();
  34. while(arrIter.hasNext()){
  35. String str = arrIter.next();
  36. if(str.equals("someSpecialValue")){
  37. arrIter.remove();
  38. }
  39. }
  40. System.out.println("t2 complete:"+arr);
  41. }
  42. };
  43. t2.start();
  44. t1.start();
  45. }
  46. }
snz8szmq

snz8szmq1#

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

xkftehaa

xkftehaa2#

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

concurrentmodificationexception与并发无关

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

  1. void example() {
  2. var list = new ArrayList<String>();
  3. list.add("a");
  4. list.add("b");
  5. for (String elem : list) {
  6. if (elem.equals("a")) list.remove(elem);
  7. }
  8. }

这是因为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——它会得到你未指定的行为,这意味着任何事情都会发生。

展开查看全部

相关问题