Java ConcurrentModificationException偶尔出现
我有一个JavaSE库和对源代码的完全访问权限,当无状态EJB在负载测试期间执行时,源代码偶尔会抛出ConcurrentModificationException 应用服务器在JDK 11.0.2上运行 最初,异常发生在封送处理期间,类似于,但我添加了对以下方法的调用,以查看异常也可能发生在何处:Java ConcurrentModificationException偶尔出现,java,multithreading,Java,Multithreading,我有一个JavaSE库和对源代码的完全访问权限,当无状态EJB在负载测试期间执行时,源代码偶尔会抛出ConcurrentModificationException 应用服务器在JDK 11.0.2上运行 最初,异常发生在封送处理期间,类似于,但我添加了对以下方法的调用,以查看异常也可能发生在何处: private static Object deepCopy(Object object) { try { ByteArrayOutputStream
private static Object deepCopy(Object object) {
try {
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
ObjectOutputStream outputStrm = new ObjectOutputStream(outputStream);
outputStrm.writeObject(object); // line 164 in MyClass
ByteArrayInputStream inputStream = new ByteArrayInputStream(outputStream.toByteArray());
ObjectInputStream objInputStream = new ObjectInputStream(inputStream);
return objInputStream.readObject();
}
catch (Exception e) {
e.printStackTrace();
return null;
}
}
以下是异常的Stacktrace示例:
java.util.ConcurrentModificationException
at java.base/java.util.ArrayList.writeObject(ArrayList.java:900)
at java.base/jdk.internal.reflect.GeneratedMethodAccessor105.invoke(Unknown Source)
at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.base/java.lang.reflect.Method.invoke(Method.java:566)
at java.base/java.io.ObjectStreamClass.invokeWriteObject(ObjectStreamClass.java:1130)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1497)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1510)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179)
at java.base/java.io.ObjectOutputStream.defaultWriteFields(ObjectOutputStream.java:1553)
at java.base/java.io.ObjectOutputStream.writeSerialData(ObjectOutputStream.java:1510)
at java.base/java.io.ObjectOutputStream.writeOrdinaryObject(ObjectOutputStream.java:1433)
at java.base/java.io.ObjectOutputStream.writeObject0(ObjectOutputStream.java:1179)
at java.base/java.io.ObjectOutputStream.writeObject(ObjectOutputStream.java:349)
at com.my.java.se.library.MyClass.deepCopy(MyClass.java:164)
at com.my.java.se.library.MyClass.someMethod(MyClass.java:132)
at com.my.thorntail.app.SomeStatelessEJB.callToSeLib(SomeStatelessEJB.java:50)
....
我在一些方法中增加了对深度拷贝的调用,而且例外有时发生在某些方法的末尾,有时在中间某个地方。
我不知道我应该从中得出什么结论。是否有其他线程决定要修改对象
现在,整个JavaSE库不包含任何关于线程的语句,因此它是纯单线程的。据我所知,在单个线程中发生ConcurrentModificationException的唯一可能的选择是,在对数组进行迭代时对其进行修改。然而,情况并非如此
这种例外可能还有什么其他原因?
我怎样才能找出哪里出了问题
根据Kayaman的回答进行编辑:
JavaSE库实际上也是我们开发的代码的一部分,因此调用代码和库代码都可能是罪魁祸首
我从使用deepCopy方法的实验中得出结论,错误就在那里。deepCopy方法导致库中抛出ConcurrentModificationException。但这可能是错误的
下面是调用代码的结构,它在负载测试期间同时在多个线程中运行。在试图将代码简化为基本部分时,我意识到了一个可能的问题:
@Stateless
public class SomeStatelessEJB {
private MyClass myClass;
public void callToSeLib() {
myClass = getMyClassInstance(); // we are reusing the same MyClass object !!
myClass.someMethod();
}
}
我将调整代码以不再重用此对象,然后在这里报告
编辑以报告进度
这确实是MyClass对象的重用
修复方法是只重用MyClass对象的部分,而为每个请求创建部分和重新创建廉价且有问题的部分的成本很高
谢谢你强迫我把代码压缩到最低限度来解释这里的情况。在这个过程中,我注意到了错误。stacktrace告诉您,在某个点上,您正在序列化一个ArrayList,同时它也在被修改。ArrayList中的相关代码 因此,要么您有来自不同线程的对象被变异,要么您已经设法编写代码,使列表通过不同的方式在同一线程中变异。在单线程的情况下,如果元素有自己的writeObject修改列表,就有可能得到这个结果。当然,在代码中这样做是很可怕的 你说这个库不使用线程,但是你仍然在基于你的环境使用线程,所以我们现在还不想给出最简单的解释 也许你应该展示一下你是如何调用MyClass.someMethod的,因为这是你的代码,而且这总是第一个疑点,在责怪你自己之前不要责怪库
// Write out element count, and any hidden stuff
int expectedModCount = modCount;
s.defaultWriteObject();
// Write out size as capacity for behavioural compatibility with clone()
s.writeInt(size);
// Write out all elements in the proper order.
for (int i=0; i<size; i++) {
s.writeObject(elementData[i]);
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}