Java反射调用newInstance触发ConcurrentModificationException导致崩溃如何解决
异常原因
ConcurrentModificationException核心触发条件是:对集合使用迭代器/增强for循环遍历的过程中,集合结构发生了增删修改,且修改操作不是通过迭代器自身的方法完成,你的代码刚好符合这个触发场景:- 事件触发时你用增强for循环遍历
event.getMethods()返回的方法列表,同时方法注册逻辑可以在任意时刻往同一个List中新增Method元素,遍历+修改同时进行就会直接抛出并发修改异常。 - 你的注册逻辑本身存在严重错误:调用
eventType.newInstance()每次都创建了全新的事件类实例,调用getMethods拿的是新实例的List,调用setMethods又是塞给另一个新实例,等于你注册的方法根本不会作用到实际触发事件时用到的事件实例上,还可能在事件实例触发遍历的同时误操作同一个List引用。 - 额外逻辑缺陷:你每次调用方法都执行
m.getDeclaringClass().newInstance(),等于每次执行事件方法都会创建一个全新的目标类对象,如果需要复用已有对象、保留成员变量状态,这个逻辑会完全失效。
- 事件触发时你用增强for循环遍历
可行实现方案
方案1:优化整体架构(推荐)
不要把方法列表存在事件实例里,单独抽一个全局的事件总线注册表,拆分事件数据和执行逻辑,从根源解决问题:
- 定义全局线程安全注册表
// key为事件类型,value为对应要执行的方法列表,用CopyOnWriteArrayList避免并发修改 private static final Map<Class<?>, List<Method>> EVENT_REGISTRY = new ConcurrentHashMap<>();
- 修复注册逻辑
for(Method m : listenable.getClass().getMethods()) { if(m.isAnnotationPresent(EventAnnotation.class)) { Parameter[] params = m.getParameters(); if(params.length != 1) { System.out.println("You need 1 event paramter!"); continue; } Class<?> eventType = params[0].getType(); EVENT_REGISTRY.computeIfAbsent(eventType, k -> new CopyOnWriteArrayList<>()).add(m); } }
- 修复事件执行逻辑
@EventHandler private Listener<EventKeyInput> eventKeyInput = new Listener<>(event -> { List<Method> methods = EVENT_REGISTRY.getOrDefault(event.getClass(), Collections.emptyList()); Object targetInstance = 你要执行方法的目标类实例; // 不要每次调用都newInstance,除非明确需要每次新建对象 for(Method m : methods) { try { m.invoke(targetInstance, event); } catch (Exception e) { e.printStackTrace(); } } });
方案2:最小改动修复
如果不想修改现有架构,直接把方法列表的实现类换成CopyOnWriteArrayList即可,这个类是Java专为读多写少的并发场景设计,遍历过程中修改集合不会抛出并发修改异常。
如果是单线程场景,也可以在遍历前复制一份列表再遍历:
// 遍历的是列表副本,原列表修改不会影响遍历过程 for(Method m : new ArrayList<>(event.getMethods()))
内容的提问来源于stack exchange,提问作者MooMooCows
相关产品推荐
相关产品推荐

