ISSUE
错误分析
DeferredApplicationEventPublisher
的继承关系
1 | import org.springframework.context.ApplicationContext; |
DeferredApplicationEventPublisher
的依赖图
现在来分析具体出现NPE
错误的原因
先看EventPublishingConfigService
中的addListener
1 |
|
然后看DelegatingEventPublishingListener
代码的继承关系
1 | import com.alibaba.nacos.api.config.ConfigService; |
可以看到,在创建DelegatingEventPublishingListener
对象的时候,会传入一个线程池Executor
,以及一个ApplicationEventPublisher
(其实就是DeferredApplicationEventPublisher
)
然后再看看CacheData.safeNotifyListener()
方法做了什么操作
1 | private void safeNotifyListener(final String dataId, final String group, final String content, final String md5, final ManagerListenerWrap listenerWrap) { |
这里看到,safeNotifyListener
是将事件广播给所有的Listener
,然后有一段及其重要的代码段,它就是导致LinkedList
出现并发使用的原因
1 | listener.getExecutor().execute(job); |
这里还记得刚刚说过的DelegatingEventPublishingListener
对象在创建之初有传入Executor
参数吗?这里Listener
调用Executor
将上述的任务调入线程池中进行调度,因此,导致了DeferredApplicationEventPublisher
可能存在并发的使用
错误复现
1 | public class DeferrNPE { |
最终修正
由于是非线程安全使用在并发的场景下,因此只能更改上层nacos-spring-context
的容器使用,将原先的非线程安全的LinkedList
转为线程安全的ConcurrentLinkedQueue