While I was testing my own answer to get the output for this question, I got the following output for the given list content:
// Add some strings into the list
list.add("Item 1");
list.add("Item 2");
list.add("Item 3");
list.add("Item 4");
Output:
Item 1
Item 2
Exception in thread "Thread-0" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:859)
at java.util.ArrayList$Itr.next(ArrayList.java:831)
at com.akefirad.tests.Main$1.run(Main.java:34)
at java.lang.Thread.run(Thread.java:745)
But if one use the following list:
// Add some strings into the list
list.add("Item 1");
list.add("Item 2");
list.add("Item 3");
Output:
Item 1
Item 2
There is no exception in the output and only two first items will be printed. Can anyone explain why it behaves like this? Thanks
Note: the code is here.
EDITED: My question is why I don't have the third item printed (meaning the list is modified) and while there is no exception.
EDITED the code to produce the exception, please note the list content:
public class Main {
public static void main(String[] args) throws InterruptedException
{
final ArrayList<String> list = new ArrayList<String>();
list.add("Item 1");
list.add("Item 2");
list.add("Item 3");
list.add("Item 4");
Thread thread = new Thread(new Runnable()
{
@Override
public void run ()
{
for (String s : list)
{
System.out.println(s);
try
{
Thread.sleep(1000);
}
catch (InterruptedException e)
{
e.printStackTrace();
}
}
}
});
thread.start();
Thread.sleep(2000);
list.remove(0);
}
}
Fundamentally, you're modifying a list in one thread while iterating over it in another, and the list implementation you're using does not support that.
The ArrayList
iterator implementation appears to only detect invalid modicifications on the call to next()
, not on the call to hasNext()
. So if you get into the last iteration of the loop before the remove()
call, then you won't get an exception - hasNext()
will just return false
. On the other hand, if the remove()
happens before the last call to next()
(and if this is noticed on the other thread - the memory model comes into play here) then you'll get the exception. So for example, if you change your in-loop sleep to Thread.sleep(2500)
then you'll get the exception at the start of the second iteration, because the remove()
call will occur before it.
If you want to use a list in multiple threads and at least one of them is modifying it, you should use an implementation which supports that, such as CopyOnWriteArrayList
.