Spring WebFlux请求构建器出现ConcurrentModificationException异常求助及推测验证
Spring WebFlux请求构建器出现ConcurrentModificationException异常求助及推测验证
大家好,我最近在Spring WebFlux项目里碰到了一个随机触发的ConcurrentModificationException,折腾了好一阵才定位到相关线索,现在把情况整理出来,想请各位帮忙看看我的推测是否准确。
首先贴一下异常的完整栈追踪:
java.util.ConcurrentModificationException at java.base/java.util.HashMap.computeIfAbsent(HashMap.java:1221) at org.springframework.web.reactive.function.client.DefaultClientRequestBuilder$BodyInserterRequest.<init>(DefaultClientRequestBuilder.java:210) at org.springframework.web.reactive.function.client.DefaultClientRequestBuilder.build(DefaultClientRequestBuilder.java:173) at org.springframework.web.reactive.function.client.DefaultWebClient$DefaultRequestBodyUriSpec.lambda$exchange$11(DefaultWebClient.java:453) at reactor.core.publisher.MonoDeferContextual.subscribe(MonoDeferContextual.java:47) at reactor.core.publisher.Mono.subscribe(Mono.java:4496) at reactor.core.publisher.MonoSubscribeOn$SubscribeOnSubscriber.run(MonoSubscribeOn.java:126) at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:84) at reactor.core.scheduler.WorkerTask.call(WorkerTask.java:37) at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264) at java.base/java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:304) at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136) at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635) at java.base/java.lang.Thread.run(Thread.java:840) Suppressed: java.lang.Exception: #block terminated with an error at reactor.core.publisher.BlockingSingleSubscriber.blockingGet(BlockingSingleSubscriber.java:103) at reactor.core.publisher.Mono.block(Mono.java:1712)
查看日志和Spring源码后,我发现异常触发的核心代码在DefaultClientRequestBuilder$BodyInserterRequest的构造方法里,具体是这一行:
Object id = attributes.computeIfAbsent(LOG_ID_ATTRIBUTE, name -> ObjectUtils.getIdentityHexString(this));
我自己琢磨了下,觉得问题可能出在这里:当前使用的attributes是一个非线程安全的HashMap,而且代码里直接使用attributes而非this.attributes,会不会导致意外引用了其他共享的Map实例?毕竟WebFlux是基于多线程调度的响应式框架,多个线程同时操作非并发安全的HashMap,很容易触发ConcurrentModificationException。
如果把这行代码改成:
Object id = this.attributes.computeIfAbsent(LOG_ID_ATTRIBUTE, name -> ObjectUtils.getIdentityHexString(this));
是不是就能确保操作的是当前实例的成员变量,避免多线程下的并发修改冲突?
另外我也考虑过,是不是应该直接把这个Map替换成ConcurrentHashMap来从根本上保证线程安全,但先想确认我的这个引用问题的推测是否正确。有没有朋友遇到过类似的问题?或者能帮忙验证下这个思路对不对?




