使用缓存避免 java 流上的 ConcurrentModificationException
Avoid ConcurrentModificationException on java stream using cache
我偶尔会 ConcurrentModificationException
使用以下代码:
public Set<MyObject> getTypes(Set<Type> names) {
Set<MyObject> myObjects = new HashSet<>();
myObjects = names.stream().filter(Objects::nonNull).mapToInt(Type::getId)
.mapToObj(cache::getMyObject)
.collect(Collectors.toSet());
我正在使用缓存转换为 MyObject
,但似乎在 collect
方法中抛出了异常(DAOImpl.java 第 114 行)
java.util.ConcurrentModificationException
at java.util.HashMap$KeySpliterator.forEachRemaining(HashMap.java:1558)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at com.dao.DAOImpl.getTypes(DAOImpl.java:114)
at com.dao.DAOImpl$$FastClassBySpringCGLIB$$affe23c4.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
at com.dao.DAOImpl$$EnhancerBySpringCGLIB$00bd7e.getTypes(<generated>)
如何使用缓存映射到对象,或者我必须在流外使用缓存?
通知缓存每天应该只更新 1 次
问题与您的 cache::getMyObject
无关。问题是您的 names
集在您流式传输时正在更改,因此 ConcurrentModificationException
.
如果您从 names
集合中包装(创建一个新集合),问题就解决了。
public Set<MyObject> getTypes(Set<Type> names) {
Set<MyObject> myObjects = new HashSet<>(names).stream().filter(Objects::nonNull).mapToInt(Type::getId)
.mapToObj(cache::getMyObject).collect(Collectors.toSet());
尽管如此,我认为您的缓存方法 (getMyObject) 可以 return null。在那种情况下,您应该将它们过滤掉。
如果你想触发异常你可以这样做:
public static void main(String[] args) {
Set<String> names = new HashSet();
names.add("a");
names.add("b");
Thread x = new Thread(){
@Override
public void run() {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
names.add("c");
}
};
x.start();
Set<Object> myObjects = new HashSet<>(names).stream().filter(Objects::nonNull).mapToInt(s -> s.hashCode()).mapToObj(i -> String.valueOf(i)).map(x1-> getx(x1)).collect(Collectors.toSet());
}
private static Object getx(String x) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return x;
}
问题是 2 个线程发生了变化 myObjects
,因此 Set
循环时的预期计数受到影响,因此出现异常
解决方法是使上述服务调用原子化,从而防止在运行时使用不同的线程更改Set
我偶尔会 ConcurrentModificationException
使用以下代码:
public Set<MyObject> getTypes(Set<Type> names) {
Set<MyObject> myObjects = new HashSet<>();
myObjects = names.stream().filter(Objects::nonNull).mapToInt(Type::getId)
.mapToObj(cache::getMyObject)
.collect(Collectors.toSet());
我正在使用缓存转换为 MyObject
,但似乎在 collect
方法中抛出了异常(DAOImpl.java 第 114 行)
java.util.ConcurrentModificationException
at java.util.HashMap$KeySpliterator.forEachRemaining(HashMap.java:1558)
at java.util.stream.AbstractPipeline.copyInto(AbstractPipeline.java:481)
at java.util.stream.AbstractPipeline.wrapAndCopyInto(AbstractPipeline.java:471)
at java.util.stream.ReduceOps$ReduceOp.evaluateSequential(ReduceOps.java:708)
at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:234)
at java.util.stream.ReferencePipeline.collect(ReferencePipeline.java:499)
at com.dao.DAOImpl.getTypes(DAOImpl.java:114)
at com.dao.DAOImpl$$FastClassBySpringCGLIB$$affe23c4.invoke(<generated>)
at org.springframework.cglib.proxy.MethodProxy.invoke(MethodProxy.java:218)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.invokeJoinpoint(CglibAopProxy.java:779)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:163)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.dao.support.PersistenceExceptionTranslationInterceptor.invoke(PersistenceExceptionTranslationInterceptor.java:137)
at org.springframework.aop.framework.ReflectiveMethodInvocation.proceed(ReflectiveMethodInvocation.java:186)
at org.springframework.aop.framework.CglibAopProxy$CglibMethodInvocation.proceed(CglibAopProxy.java:750)
at org.springframework.aop.framework.CglibAopProxy$DynamicAdvisedInterceptor.intercept(CglibAopProxy.java:692)
at com.dao.DAOImpl$$EnhancerBySpringCGLIB$00bd7e.getTypes(<generated>)
如何使用缓存映射到对象,或者我必须在流外使用缓存?
通知缓存每天应该只更新 1 次
问题与您的 cache::getMyObject
无关。问题是您的 names
集在您流式传输时正在更改,因此 ConcurrentModificationException
.
如果您从 names
集合中包装(创建一个新集合),问题就解决了。
public Set<MyObject> getTypes(Set<Type> names) {
Set<MyObject> myObjects = new HashSet<>(names).stream().filter(Objects::nonNull).mapToInt(Type::getId)
.mapToObj(cache::getMyObject).collect(Collectors.toSet());
尽管如此,我认为您的缓存方法 (getMyObject) 可以 return null。在那种情况下,您应该将它们过滤掉。
如果你想触发异常你可以这样做:
public static void main(String[] args) {
Set<String> names = new HashSet();
names.add("a");
names.add("b");
Thread x = new Thread(){
@Override
public void run() {
try {
sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
names.add("c");
}
};
x.start();
Set<Object> myObjects = new HashSet<>(names).stream().filter(Objects::nonNull).mapToInt(s -> s.hashCode()).mapToObj(i -> String.valueOf(i)).map(x1-> getx(x1)).collect(Collectors.toSet());
}
private static Object getx(String x) {
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
return x;
}
问题是 2 个线程发生了变化 myObjects
,因此 Set
循环时的预期计数受到影响,因此出现异常
解决方法是使上述服务调用原子化,从而防止在运行时使用不同的线程更改Set