简体   繁体   中英

Why this code doesn't throw ConcurrentModificationException when multiple threads work on same arraylist at the same time using iterator

Here 2 threads work on same arraylist and one thread read the elements and another thread remove a specific element. I expect this to throw ConcurrentModificationException . But it is not throwing why?

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();
    }


}

You have overridden start methods for both thread instances instead of run , and these methods complete within the main execution thread, thus, no simultaneous thread execution takes place and no ConcurrentModificationThreadException is able to occur here.

You've made 2 somewhat common mistakes.

ConcurrentModificationException is not about concurrency

You'd think, given the name, that CoModEx is about concurrency. It's not. As in, you don't need threads to get it. Here, this trivial code will throw it:

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

That's becauseCoModEx is thrown by iterators and simply means this happened:

  1. Somebody made an iterator.
  2. Somebody changed the list somehow (and not via the iterator's.remove() method)
  3. Somebody runs any relevant method on the iterator made in #1

So, in the above, the foreach loop implicitly makes an iterator (#1), then the list.remove method is invoked (#2), then by hitting the foreach loop again, we call a relevant method on that iterator ( .hasNext() ), and, voila, CoModEx occurs.

In fact, multithreaded is less likely : After all, you should assume that if you interact with some object from multiple threads, that it is broken , in that behaviour is unspecified, thus, you have a bug, and worse, a hard to test for one. If you modify a plain jane arraylist from another thread whilst iterating over it, you are not guaranteed a CoModEx. You may get it. You may not. The computer may walk off the desk and try its luck on broadway. "Unspecified behaviour" is a nice way of saying: "Don't, seriously. It'll hurt the whole time because you cannot test it; this will work fine the entire time you are developing it, and juuust as you're giving that important demo to big wig client, it'll fail on you, in embarassing ways".

The way to interact with one object from multiple threads is very carefully: Check the docs of the specific object explicitly states what happens (ie use stuff from the java.util.concurrent package which is specifically designed with 'interact with it from more than one thread' use cases in mind), and failing that, use locking. These are tricky things, so the usual way to do multi-threading in java is to not have shared state in the first place. Isolate as much as you can, invert control, and use messaging strategies that have built-in transactional intrinsics, such as message queues (rabbitmq and friends) and databases (which have transactions).

How to use Threads

You override the run() method, and then start the thread by calling the start method. Or better yet, don't override run, pass a Runnable instance along as you create the thread instance.

That's how you use thread. You didn't - you overrode start, which means starting these threads doesn't make a new thread at all, it just runs the payload in your thread. That explains your specific case, but what you're trying to do (witness CoModEx by messing with a list from another thread) doesn't get you a CoModEx either - it gets you unspecified behaviour, which means anything goes.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM