• springboot @EnableScheduling 启用多线程


    现象

    使用@EnableScheduling注解后,可以发现所有任务都排队执行,并且调度器线程名称都是“taskScheduler-1”

    原因

    默认配置使用单线程调度器

    解决方案

    配置线程池任务调度器

    @Bean
    public TaskScheduler taskScheduler() {
        ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();
        taskScheduler.setPoolSize(10);
        return taskScheduler;
    }
    

    源码分析

    • org.springframework.scheduling.annotation.EnableScheduling
    // 导入配置
    @Import(SchedulingConfiguration.class)
    
    • org.springframework.scheduling.annotation.SchedulingConfiguration
    // 注册调度注解处理器
    @Bean(name = TaskManagementConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
    @Role(BeanDefinition.ROLE_INFRASTRUCTURE)
    public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
    	return new ScheduledAnnotationBeanPostProcessor();
    }
    
    • org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor
    // 为registrar注入调度器实例
    private void finishRegistration() {
    	if (this.scheduler != null) {
    		this.registrar.setScheduler(this.scheduler);
    	}
    
    	if (this.beanFactory instanceof ListableBeanFactory) {
    		Map<String, SchedulingConfigurer> beans =
    				((ListableBeanFactory) this.beanFactory).getBeansOfType(SchedulingConfigurer.class);
    		List<SchedulingConfigurer> configurers = new ArrayList<>(beans.values());
    		AnnotationAwareOrderComparator.sort(configurers);
    		for (SchedulingConfigurer configurer : configurers) {
    			configurer.configureTasks(this.registrar);
    		}
    	}
    
    	if (this.registrar.hasTasks() && this.registrar.getScheduler() == null) {
    		// 从beanFactory查找调度器实例注入
    		Assert.state(this.beanFactory != null, "BeanFactory must be set to find scheduler by type");
    		try {
    			// Search for TaskScheduler bean...
    			this.registrar.setTaskScheduler(resolveSchedulerBean(this.beanFactory, TaskScheduler.class, false));
    		}
    		catch (NoUniqueBeanDefinitionException ex) {
    			logger.trace("Could not find unique TaskScheduler bean", ex);
    			try {
    				this.registrar.setTaskScheduler(resolveSchedulerBean(this.beanFactory, TaskScheduler.class, true));
    			}
    			catch (NoSuchBeanDefinitionException ex2) {
    				if (logger.isInfoEnabled()) {
    					logger.info("More than one TaskScheduler bean exists within the context, and " +
    							"none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' " +
    							"(possibly as an alias); or implement the SchedulingConfigurer interface and call " +
    							"ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback: " +
    							ex.getBeanNamesFound());
    				}
    			}
    		}
    		catch (NoSuchBeanDefinitionException ex) {
    			logger.trace("Could not find default TaskScheduler bean", ex);
    			// Search for ScheduledExecutorService bean next...
    			try {
    				this.registrar.setScheduler(resolveSchedulerBean(this.beanFactory, ScheduledExecutorService.class, false));
    			}
    			catch (NoUniqueBeanDefinitionException ex2) {
    				logger.trace("Could not find unique ScheduledExecutorService bean", ex2);
    				try {
    					this.registrar.setScheduler(resolveSchedulerBean(this.beanFactory, ScheduledExecutorService.class, true));
    				}
    				catch (NoSuchBeanDefinitionException ex3) {
    					if (logger.isInfoEnabled()) {
    						logger.info("More than one ScheduledExecutorService bean exists within the context, and " +
    								"none is named 'taskScheduler'. Mark one of them as primary or name it 'taskScheduler' " +
    								"(possibly as an alias); or implement the SchedulingConfigurer interface and call " +
    								"ScheduledTaskRegistrar#setScheduler explicitly within the configureTasks() callback: " +
    								ex2.getBeanNamesFound());
    					}
    				}
    			}
    			catch (NoSuchBeanDefinitionException ex2) {
    				logger.trace("Could not find default ScheduledExecutorService bean", ex2);
    				// Giving up -> falling back to default scheduler within the registrar...
    				logger.info("No TaskScheduler/ScheduledExecutorService bean found for scheduled processing");
    			}
    		}
    	}
    
    	this.registrar.afterPropertiesSet();
    }
    
    • org.springframework.scheduling.config.ScheduledTaskRegistrar
    // 配置调度任务
    protected void scheduleTasks() {
    	// 如果没有任务调度器,则创建单线程调度器
    	if (this.taskScheduler == null) {
    		this.localExecutor = Executors.newSingleThreadScheduledExecutor();
    		this.taskScheduler = new ConcurrentTaskScheduler(this.localExecutor);
    	}
    	if (this.triggerTasks != null) {
    		for (TriggerTask task : this.triggerTasks) {
    			addScheduledTask(scheduleTriggerTask(task));
    		}
    	}
    	if (this.cronTasks != null) {
    		for (CronTask task : this.cronTasks) {
    			addScheduledTask(scheduleCronTask(task));
    		}
    	}
    	if (this.fixedRateTasks != null) {
    		for (IntervalTask task : this.fixedRateTasks) {
    			addScheduledTask(scheduleFixedRateTask(task));
    		}
    	}
    	if (this.fixedDelayTasks != null) {
    		for (IntervalTask task : this.fixedDelayTasks) {
    			addScheduledTask(scheduleFixedDelayTask(task));
    		}
    	}
    }
    
    • java.util.concurrent.Executors
    // 创建线程池容量为1的调度器
    public static ScheduledExecutorService newSingleThreadScheduledExecutor() {
     return new DelegatedScheduledExecutorService
            (new ScheduledThreadPoolExecutor(1));
    }
    
  • 相关阅读:
    归并排序
    希尔排序
    字符串操作
    引用
    直接插入排序
    变量赋值
    C#中关于公共类的使用
    关于SQL中Between语句查询日期的问题
    用户控件 与 重写控件 的区别
    什么是命名空间,为什么要使用命名空间?
  • 原文地址:https://www.cnblogs.com/luguojun/p/14294666.html
Copyright © 2020-2023  润新知