Reputation: 328608
I read in a few posts that using JUnit to test concurrency is not ideal but I have no choice for now. I have just encountered an exception that I can't explain.
I run a test where, in summary:
What could cause that behavior?
Note: I only get the exception from time to time. The code has some non related stuff but I left it there in case I missed something. XXXQuery
is an enum.
public void testConcurrent() throws InterruptedException {
final int N_THREADS = 1000;
final XXXData xxxData = new AbstractXXXDataImpl();
final List<QueryResult> results = new ArrayList<>();
ExecutorService executor = Executors.newFixedThreadPool(N_THREADS);
for (int i = 0; i < N_THREADS; i++) {
final int j = i;
executor.submit(new Runnable() {
@Override
public void run() {
try {
results.add(xxxData.get(XXXQuery.values()[j % XXXQuery.values().length]));
} catch (Exception e) {
System.out.println(e);
}
}
});
}
executor.shutdown();
executor.awaitTermination(10, TimeUnit.SECONDS);
assertEquals(N_THREADS, results.size());
}
Upvotes: 0
Views: 171
Reputation: 116878
You cannot add to the results
ArrayList
in your Runnable.run()
method in multiple threads without synchronizing around it.
The assertion failed message is showing that although N_THREADS
calls to add()
were made, the ArrayList
got fewer entries because of concurrency race conditions.
I would use a final array instead of a list. Something like:
final QueryResult[] results = new QueryResult[N_THREADS];
for (int i = 0; i < N_THREADS; i++) {
...
public void run() {
results[j] = data.get(Query.values()[j % Query.values().length]);
}
Also, I don't quite get the XXXQuery.values()
but I'd pull that into a variable above the loop unless it is changing.
Upvotes: 2