gpt4 book ai didi

java - Callable 类中的并发修改异常

转载 作者:行者123 更新时间:2023-12-02 02:17:35 25 4
gpt4 key购买 nike

我试图将对象列表拆分为较小的子列表,并在不同的线程上单独处理它们。所以我有以下代码:

        List<Instance> instances = xmlInstance.readInstancesFromXml();
List<Future<List<Instance>>> futureList = new ArrayList<>();

int nThreads = 4;

ExecutorService executor = Executors.newFixedThreadPool(nThreads);

final List<List<Instance>> instancesPerThread = split(instances, nThreads);

for (List<Instance> instancesThread : instancesPerThread) {
if (instancesThread.isEmpty()) {
break;
}

Callable<List<Instance>> callable = new MyCallable(instancesThread);
Future<List<Instance>> submit = executor.submit(callable);
futureList.add(submit);
}

instances.clear();

for (Future<List<Instance>> future : futureList) {
try {
final List<Instance> instancesFromFuture = future.get();
instances.addAll(instancesFromFuture);
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
}
}

executor.shutdown();

try {
executor.awaitTermination(Long.MAX_VALUE, TimeUnit.NANOSECONDS);
} catch (InterruptedException ie) {
ie.printStackTrace();
}

和 MyCallable 类:

public class MyCallable implements Callable<List<Instance>> {

private List<Instance> instances;

public MyCallable (List<Instance> instances) {
this.instances = Collections.synchronizedList(instances);
}


@Override
public List<Instance> call() throws Exception {

for (Instance instance : instances) {
//process each object and changing some fields;
}

return instances;
}

}

拆分方法(它将给定列表拆分为给定数量的列表,并尝试使每个子列表的大小几乎相同):

public static List<List<Instance>> split(List<Instance> list, int nrOfThreads) {
List<List<Instance>> parts = new ArrayList<>();
final int nrOfItems = list.size();
int minItemsPerThread = nrOfItems / nrOfThreads;
int maxItemsPerThread = minItemsPerThread + 1;
int threadsWithMaxItems = nrOfItems - nrOfThreads * minItemsPerThread;
int start = 0;
for (int i = 0; i < nrOfThreads; i++) {
int itemsCount = (i < threadsWithMaxItems ? maxItemsPerThread : minItemsPerThread);
int end = start + itemsCount;
parts.add(list.subList(start, end));
start = end;
}

return parts;
}

所以,当我尝试执行它时,我在这一行得到 java.util.ConcurrentModificationException for (Instance instance :instances) {,有人可以给出任何想法为什么会发生这种情况吗?

最佳答案

public MyCallable (List<Instance> instances) {
this.instances = Collections.synchronizedList(instances);
}

使用synchronizedList就像这并不能以您想象的方式帮助您。

仅将列表包装在 synchronizedList 中才有用。在您创建它时(例如 Collections.synchronizedList(new ArrayList<>()) 。否则,底层列表是可以直接访问的,因此可以以不同步的方式访问。

此外,synchronizedList仅在单个方法调用期间同步,而不是在迭代它的整个时间内同步。

这里最简单的解决方法是在构造函数中获取列表的副本:

    this.instances = new ArrayList<>(instances);

然后,其他人都无法访问该列表,因此在您迭代它时他们无法更改它。

这与获取 call 中的列表副本不同。方法,因为复制是在代码的单线程部分完成的:在您获取该副本时,没有其他线程可以修改它,因此您不会得到 ConcurrentModificationException (您可以在单线程代码中获取 CME,但不能使用此复制构造函数)。在 call 中进行复制方法意味着列表被迭代,其方式与for完全相同。循环你已经有了。

关于java - Callable 类中的并发修改异常,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49066208/

25 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com