初始化 Spring Bean 时的竞争条件

Race condition when initialized Spring Bean

我正在使用 Java Spring 配置,很少有 bean 引用一个 bean。似乎所有 bean 都试图同时初始化 properties() bean,因为在服务启动时出现 ConcurrentModificationException(并非总是如此。1000 次中有 1 次出现此错误)-

ERR [main                    ] - org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'genericTaskExecutor' defined in class path resource [com/test/SpringConfiguration.class]: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [java.util.concurrent.Executor]: Factory method 'threadPoolTaskExecutor' threw exception; nested exception is java.util.ConcurrentModificationException
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:655)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiateUsingFactoryMethod(ConstructorResolver.java:483)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.instantiateUsingFactoryMethod(AbstractAutowireCapableBeanFactory.java:1338)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBeanInstance(AbstractAutowireCapableBeanFactory.java:1177)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:557)
    at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:517)
    at org.springframework.beans.factory.support.AbstractBeanFactory.lambda$doGetBean[=11=](AbstractBeanFactory.java:323)
    at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:226)
    at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:321)
    at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:202)
    at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:893)
    at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:879)
    at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:551)
    at org.springframework.context.support.FileSystemXmlApplicationContext.<init>(FileSystemXmlApplicationContext.java:142)
    at org.springframework.context.support.FileSystemXmlApplicationContext.<init>(FileSystemXmlApplicationContext.java:95)
    at Main.start(Main.java:27)
Caused by: org.springframework.beans.BeanInstantiationException: Failed to instantiate [java.util.concurrent.Executor]: Factory method 'threadPoolTaskExecutor' threw exception; nested exception is java.util.ConcurrentModificationException
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:185)
    at org.springframework.beans.factory.support.ConstructorResolver.instantiate(ConstructorResolver.java:650)
    ... 16 more
Caused by: java.util.ConcurrentModificationException
    at java.util.Hashtable$Enumerator.next(Hashtable.java:1387)
    at java.util.Hashtable.putAll(Hashtable.java:523)
    at com.test.SpringConfiguration.properties(DependencySpringConfiguration.java:43)
    at com.test.SpringConfiguration.threadPoolTaskExecutor(DependencySpringConfiguration.java:69)
    at com.test.SpringConfiguration$$EnhancerBySpringCGLIB$$c6bbb94b.CGLIB$threadPoolTaskExecutor[=11=](<generated>)
    at com.test.SpringConfiguration$$EnhancerBySpringCGLIB$$c6bbb94b$$FastClassBySpringCGLIB$cff70.invoke(<generated>)
    at org.springframework.cglib.proxy.MethodProxy.invokeSuper(MethodProxy.java:244)
    at org.springframework.context.annotation.ConfigurationClassEnhancer$BeanMethodInterceptor.intercept(ConfigurationClassEnhancer.java:331)
    at com.test.SpringConfiguration$$EnhancerBySpringCGLIB$$c6bbb94b.threadPoolTaskExecutor(<generated>)
    at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
    at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.lang.reflect.Method.invoke(Method.java:498)
    at org.springframework.beans.factory.support.SimpleInstantiationStrategy.instantiate(SimpleInstantiationStrategy.java:154)
    ... 17 more

以下是 bean 定义。我的代码在调用“properties.putAll(System.getProperties())”时出错。我无法在 Spring 配置中找到我缺少的东西,因为异常很少出现所以必须是 bean 初始化之间的竞争条件 -

@Configuration
@EnableScheduling
public class DependencySpringConfiguration { 
    @Bean
    public static Properties properties() {
        Properties properties = new Properties();
        properties.putAll(System.getProperties());
        //do some formatting on properties data
        return properties;
    }

    @Bean(name = "genericTaskExecutor")
    @Primary
    public Executor threadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        
        int corePoolSize = NumberUtils.toInt(properties().getProperty("generic-executor.core-pool.size"), 10);
        log.info("Initial pool size for 'genericTaskExecutor' is {}", corePoolSize);
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(50);
        executor.setThreadNamePrefix("GenericTaskExecutor-");
        executor.initialize();
        return executor;
    }

    @Bean(name = "legacy")
    public Executor legacyThreadPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        int corePoolSize = NumberUtils.toInt(properties().getProperty("legacy-executor.core-pool.size"), 1);
        log.info("Initial pool size for 'legacyTaskExecutor' is {}", corePoolSize);
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(10);
        executor.setThreadNamePrefix("LegacyTaskExecutor-");
        executor.initialize();
        return executor;
    }

    @Bean(name = "reconTaskExecutor")
    public Executor reconPoolTaskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        int corePoolSize = NumberUtils.toInt(properties().getProperty("recon-executor.core-pool.size"), 2);
        log.info("Initial pool size for 'reconTaskExecutor' is {}", corePoolSize);
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(10);
        executor.setThreadNamePrefix("ReconTaskExecutor-");
        executor.initialize();
        return executor;
    }

}

系统属性可以随时更改,因此您对 properties.putAll(System.getProperties()) 的尝试会抛出 ConcurrentModificationException。那是因为 putAll 方法使用了一个迭代器。尝试 clone() 系统属性并改用克隆的实例。

properties.putAll(System.getProperties().clone());