Reputation: 319
public class TestConcurrentForList {
List<Integer> mainList = new ArrayList<Integer>();
ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(1);
Random r = new Random();
public void start() throws InterruptedException {
Runnable cmd = new Runnable() {
@Override
public void run() {
List<Integer> tempList = mainList;
mainList = new ArrayList<Integer>();
for (Integer i: tempList) {
System.out.println("subThread:" + i);
}
}
};
scheduledExecutorService.scheduleAtFixedRate(cmd, 1, 1, TimeUnit.MILLISECONDS);
while (true) {
mainList.add(r.nextInt(200));
Thread.sleep(100);
}
}
public static void main(String[] args) {
TestConcurrentForList tester = new TestConcurrentForList();
try {
tester.start();
} catch (Exception e) {
e.printStackTrace();
System.err.println(e.getMessage());
}
}
}
Part of our product code likes this, the main thread and subthread share the mainList. I run the test serval times but never reproduce the ConcurrentModificationException.
update:
thanks for all your replying,this code is actually a brief abstraction of our production code. What I wanna do actually is very simply:
the main thread hold a list to receive data from some source, when the list reaches a certain size the main thread pass the list to a sub thread which stores the data to a data base.
Maybe a more safer way to accomplish this is to extract the
List<Integer> tempList = mainList;
mainList = new ArrayList<Integer>();
to the main thread, and pass the templist to sub thread. The code I list before is a legacy code, and I want to fix this code.
Upvotes: 1
Views: 132
Reputation: 25855
As David Wallace points out, you need to at least declare mainList
as volatile
.
However, that alone does not actually make the code thread-safe. Even though you're switching the reference in the cmd
thread, the main thread may have already fetched the reference before that happens and can then proceed to work on it at the same time as the cmd
thread reads from it.
For example, this is a possible sequence of events:
cmd
thread fetches mainList
reference and gets list AmainList
reference and also gets list Acmd
thread creates the new list, B, and assigns it to mainList
cmd
thread starts iterating over list Acmd
thread continues to iterate over the modified list, now in an inconsistent state due to concurrent modificationEDIT: At this point, I was planning to edit in a suggestion to do what you wanted, but I realized there could be several quite different things that you wanted to do with this code, so a single suggestion would just be a guess anyway. If you want a solution, I suggest you start a new question describing your goal in more detail.
Upvotes: 2
Reputation: 423
No, I do not think the code is thread safe because the main thread could call List.add while the pool thread is assigning a new value to mainList. If mainList where a primative, it might be sufficient to make it 'volatile'. But I don't think you can use 'volatile' with object references.
To make the assignment safe, you would need to synchronize on something while you make the assignment and then also wherever you try to touch mainList like:
Object lock = new Object();
...
synchronized (lock) {
mainList = new ArrayList<Integer>();
}
...
synchronized (lock) {
mainList.add(r.nextInt(200));
}
This would ensure that the pool thread could not reassign mainList while the main thread was in the process of calling add().
But I'm not sure if you can get a ConcurrentModificationException if only the main thread modifies the list and the pool thread is only iterating through elements. And even if the pool thread did modify the list I'm still not sure if you could get a CME if the pool thread modified a new list that has not yet been assigned to mainList.
So if you are seeing a CME, I suspect your test does not really represent what is happening in production.
Upvotes: 0
Reputation: 26882
No it's not thread safe. You are not using any synchronization facilities around mainList
. The fact that the code didn't throw ConcurrentModificationException
does not imply that the code is thread-safe. It merely means you might have a race condition if it's thrown.
Upvotes: 0