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've made 2 somewhat common mistakes.
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:
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 (i.e. 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).
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.