gpt4 book ai didi

java - 删除同步方法中的 ArrayList 元素

转载 作者:行者123 更新时间:2023-12-02 11:38:10 24 4
gpt4 key购买 nike

我有一个 synchronized 方法,该方法对 ArrayList 进行操作,然后通过调用 ArrayList.clear() 删除其元素。

当多个线程调用此方法时,即使它是同步的,为什么我会收到 ConcurrentModificationException

关键字synchronized不是用来避免这种错误的吗?

public MyOuterClass {

private class MyInnerClass implements MyCallback {

private class MyInnermostClass implements Runnable {

@Override
public void run() {

if (condition) {
myMethod();
}
else {
synchronized (myArrayList) {
myArrayList.add(new MyObject());
}
}
}

public void myCallback() {
Thread myThread = new Thread(new MyInnermostClass());
myThread.start();
}
}

private ArrayList<MyObject> myArrayList;

public MyClass() {
myArrayList = new ArrayList<>();
}

private synchronized void myMethod() {
for (MyObject o: myArrayList) {
System.out.println(o);
}
myArrayList.clear();
}
}

堆栈跟踪:

Exception in thread "Thread-9" java.util.ConcurrentModificationException
at java.base/java.util.ArrayList$Itr.checkForComodification(ArrayList.java:939)
at java.base/java.util.ArrayList$Itr.next(ArrayList.java:893)
at my.package.MyOuterClass.myMethod(MyOuterClass.java:myLineNumber)

最佳答案

如果您的方法是从多个线程调用的,那么它肯定需要同步。然而,听起来好像发生了其他事情。

尽管有它的名字,ConcurrentModificationException 与线程并发性(本质上)无关。相反,当迭代器注意到它正在迭代的集合以某种意外的方式发生更改时,就会抛出该异常。例如,以下内容将生成 ConcurrentModificationException,即使对于单线程应用程序也是如此:

for (Object o : myArrayList) {
if (shouldRemove(o)) {
myArrayList.remove(o);
}
}

处理这种情况的正确方法是使用显式迭代器并用它来删除元素:

for (Iterator<Object> iter = myArrayList.iterator(), Object o;
iter.hasNext();
o = iter.next())
{
if (shouldRemove(o)) {
iter.remove();
}
}

您还可以使用ListIterator(通过myArrayList.listIterator()获得)在迭代时获得对列表内容的更多控制(例如替换元素) .

您的方法需要同步的原因是,否则一个线程可能会调用myArrayList.clear(),而另一个线程仍在迭代,从而导致异常。同时调用 myArrayList.clear() 也有可能破坏列表的内部结构。

由于您的方法是同步的,我怀疑您存在以下一个(或两个)问题:

  1. 您正在另一个线程上同时修改尚未发布的其他代码中的列表;
  2. 您正在修改列表的 for 循环体内执行某些操作(除了调用 System.out.println(o) 之外)。

其中任何一个都会导致异常。对于第一个问题,您需要确保修改myArrayList的每一段代码都是同步的。不仅如此,它们还需要在同一个对象上同步。对于第二个问题,要么安排代码在迭代器完成后进行列表修改,要么使用显式迭代器并使用它进行所有更改。

编辑根据您编辑的问题,这是我的诊断:您正在同步不同对象上的不同代码块,因此您仍在多个线程上同时执行代码。具体来说,在 MyInnermostClass 中,您正在 myArrayList 上进行同步。在 MyClass 中,同步的是 myMethod() 本身,它在 MyClass 的实例上同步,而不是在数组上同步。我认为最好的办法是重新定义 myMethod(),如下所示:

private void myMethod() {
synchronized (myArrayList) {
for (MyObject o: myArrayList) {
System.out.println(o);
}
myArrayList.clear();
}
}

这样,所有修改 myArrayList 的代码都会在列表对象本身上同步。

关于java - 删除同步方法中的 ArrayList 元素,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/48775471/

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