尽管使用同步对集合进行排序,但仍获取 ConcurrentModificationException

Getting ConcurrentModificationException despite using synchronized to sort a Collection

我尝试使用线程方法对集合进行排序,其中方法单独调用比较器 classes

public class ThreadedSort{
   public ThreadedSort(){
       ThreadedIDSort idSort=new ThreadedIDSort();
       ThreadedNameSort nameSort=new ThreadedNameSort();
       ThreadedSalarySort salarySort=new ThreadedSalarySort();

        ExecutorService threadExecutor=Executors.newCachedThreadPool();

        threadExecutor.execute(idSort);
        threadExecutor.execute(nameSort);
        threadExecutor.execute(salarySort);

   }
}

每个线程方法如下所示:

public class ThreadedIDSort implements Runnable, EmployeeInterface {
    public synchronized void run(){
        employeeList.sort(new IDComparator());
    }
  }

ID Compartator class如下:

public class IDComparator implements Comparator<Employee> {
    @Override
    public int compare(Employee a, Employee b) {
        return a.getID()-b.getID();
    }

}

employeeList 是具有属性 name、id、salary 和 post:

的对象列表
ArrayList<Employee> employeeList=new ArrayList<>();

虽然我在 运行 方法之前添加了同步,但编辑列表仍然会出现 ConcurrentModificationException

Exception in thread "pool-2-thread-2" Exception in thread "JavaFX Application Thread" java.util.ConcurrentModificationException
    at java.base/java.util.ArrayList.sort(ArrayList.java:1723)
    at Client.Sort.Threaded.ThreadedNameSort.run(ThreadedNameSort.java:9)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1130)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:630)
    at java.base/java.lang.Thread.run(Thread.java:831)

我正在尝试使用线程同时使用名称、post 和 ID 进行排序。

A ConcurrentModification 也可以出现在单线程应用程序中。当集合被修改和迭代时,它们就会发生。这可以像下面这样简单:

for (String s : myStringList) {
    if ("X".equals(s)) {
        myStringList.remove(s);
    }
}

请注意,来自 java.util.concurrent 的 so-called 并发集合旨在支持这一点,但来自 java.util 的大多数(全部?)都有同样的问题,即使你应用同步。

没有看到 employeeList 到底是什么,以及 IDComparator 做了什么,很难说为什么在这种特定情况下抛出异常。

Rob Spoor 解释了为什么 ConcurrentModificationException 不一定与线程有关。而且,你应该知道这一点:

您示例中 run() 方法的 synchronized 关键字无效。

当你写一个同步方法时,

    public synchronized void bar() {
        ...
    }

这和你写的一样,

    public void bar() {
        synchronized(this) {
             ...
        }
    }

在您的示例中,您提交给执行程序服务的三个任务中的每一个都是不同的 Runnable 对象。关键字 this 在三个 run() 方法中分别引用不同的对象。三个不同的线程在三个不同的对象上同步就等于根本没有同步。

仅当线程在相同 对象上同步时,同步才有意义。规则是,不允许两个线程同时在 同一对象 上同步。

问题是相同的 'employeeList' 被 运行 不同的线程同时修改。

其中一个选项是克隆 employeeList 并分配单独的列表进行排序。

示例:

ArrayList<Employee> employeeList=new ArrayList<>();

ArrayList<Employee>  employeeListByName = employeeList.clone();

ArrayList<Employee>  employeeListBySalary = employeeList.clone();

// use copy of list in respective thread

ThreadedIDSort  -> use employeeList
ThreadedNameSort -> use employeeListByName
ThreadedSalarySort -> use employeeListBySalary