JDK1.8中Collections.sort()抛出ConcurrentModificationException的解决方法
解决ConcurrentModificationException异常的方案
异常原因
你遇到的ConcurrentModificationException是因为对ModuleManager.modules集合执行排序操作时,该集合被其他线程同时修改(比如模块启用/禁用时添加或移除元素),或是单线程下遍历过程中集合结构发生了变化。Collections.sort和List.sort内部会遍历集合,此时集合的结构修改会触发这个异常。
解决方案
方案1:操作集合副本而非原集合
创建原集合的快照副本,对副本进行排序和后续渲染操作,避免原集合的修改干扰排序过程:
public void onRender() { // 创建原集合的副本 List<BaseModule> moduleCopy = new ArrayList<>(ModuleManager.modules); // 对副本执行排序 Collections.sort(moduleCopy, new ModuleComparator()); // 后续渲染逻辑使用moduleCopy而非原集合 }
也可以用Stream API简化写法:
import java.util.stream.Collectors; public void onRender() { List<BaseModule> sortedModules = ModuleManager.modules.stream() .sorted(new ModuleComparator()) .collect(Collectors.toList()); // 使用sortedModules完成渲染 }
方案2:加锁保证线程安全
给ModuleManager.modules的所有访问操作(读取、修改、排序)加同步锁,确保同一时间只有一个线程操作集合:
- 在
ModuleManager中定义全局锁对象:
public class ModuleManager { public static List<BaseModule> modules = new ArrayList<>(); // 定义用于同步的锁对象 public static final Object MODULE_LOCK = new Object(); }
- 排序时加锁:
public void onRender() { synchronized(ModuleManager.MODULE_LOCK) { Collections.sort(ModuleManager.modules, new ModuleComparator()); } }
- 所有修改
modules的地方(比如添加/删除模块)必须同步加锁:
// 示例:添加模块时的加锁逻辑 synchronized(ModuleManager.MODULE_LOCK) { ModuleManager.modules.add(newModule); }
方案3:改用线程安全集合
使用CopyOnWriteArrayList替代普通ArrayList,它的迭代器基于集合快照,不会抛出ConcurrentModificationException,适合读多写少的场景(比如模块开关操作频率低):
// 在ModuleManager中修改集合类型 import java.util.concurrent.CopyOnWriteArrayList; public class ModuleManager { public static List<BaseModule> modules = new CopyOnWriteArrayList<>(); }
之后直接对原集合排序即可,无需额外处理:
public void onRender() { Collections.sort(ModuleManager.modules, new ModuleComparator()); }
额外修复:Comparator逻辑bug
你原有的ModuleComparator存在逻辑错误,两个if判断的条件完全相同,会导致排序结果不符合预期,修复如下:
public static class ModuleComparator implements Comparator<BaseModule> { @Override public int compare(BaseModule first, BaseModule second) { Minecraft mc = Minecraft.getMinecraft(); int firstWidth = mc.fontRendererObj.getStringWidth(first.name); int secondWidth = mc.fontRendererObj.getStringWidth(second.name); if(firstWidth > secondWidth) { return -1; // 宽度大的模块排前面 } else if(firstWidth < secondWidth) { return 1; // 宽度小的模块排后面 } return 0; } }
内容的提问来源于stack exchange,提问作者nekettt
相关产品推荐
相关产品推荐

