我在几篇文章中读到,使用 JUnit 测试并发性并不理想,但我现在别无选择。我刚刚遇到了一个我无法解释的异常。
我运行一个测试,总结一下:
- 我向遗嘱执行人提交 1000 个可运行项
- 每个可运行对象将一个元素添加到列表中
- 我等待遗嘱执行人终止
- JUnit告诉我列表只有999个元素
- 在可运行的捕获块中打印无异常
什么可能导致这种行为?
注意:我只是不时收到异常。代码有一些不相关的东西,但我把它留在那里,以防我错过了什么。 XXXQuery
是一个枚举。
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());
}
如果不围绕Runnable.run()
方法进行同步,则无法在多个线程中向results
ArrayList
添加。
断言失败消息显示,尽管对add()
进行了N_THREADS
调用,但由于并发争用条件,ArrayList
获得的条目较少。
我会使用最终数组而不是列表。 像这样:
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]);
}
另外,我不太明白XXXQuery.values()
但我会将其拉到循环上方的变量中,除非它正在发生变化。