SpringBoot中的定时任务是通过`@Scheduled`注解和`ScheduledTaskRegistrar`类来实现的。首先,我们需要在`Application`类上添加`@EnableScheduling`注解来启用定时任务功能,并通过`@SpringBootApplication`注解排除`DataSourceAutoConfiguration.class`,以避免数据源自动配置影响到我们的定时任务。

```java

import org.springframework.boot.SpringApplication;

import org.springframework.boot.autoconfigure.SpringBootApplication;

import org.springframework.scheduling.annotation.EnableScheduling;

@SpringBootApplication

@EnableScheduling

public class Application {

public static void main(String[] args) {

SpringApplication.run(Application.class, args);

}

}

```

接下来,我们创建一个名为`OtherScheduler`的类,并使用`@Slf4j`和`@Component`注解进行标注。在这个类中,我们定义了两个方法:`print()`和`print5()`,分别用于打印每10秒和每5秒的信息。这两个方法都使用了`@Scheduled`注解,并设置了相应的cron表达式。

```java

import org.slf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Component;

@Component

public class OtherScheduler {

private static final Logger log = LoggerFactory.getLogger(OtherScheduler.class);

@Scheduled(cron = "0/10 * * * * ?")

public void print() {

log.info("每10S打印一次");

}

@Scheduled(cron = "0/5 * * * * ?")

public void print5() {

log.info("每5S打印一次");

}

}

```

原理分析:当Spring Boot应用启动时,会自动扫描项目中的组件,找到带有`@Scheduled`注解的方法,并将它们添加到一个名为`ScheduledTaskRegistrar`的列表中。这个列表是由`ScheduledAnnotationBeanPostProcessor`类生成的,它负责处理这些注解,并将它们转换为真正的定时任务。当应用程序运行时,调度器会根据cron表达式的规则来触发这些定时任务。

以下是重构后的代码:

```java

public void afterPropertiesSet() {

scheduleTasks();

}

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));

}

}

}

private void addScheduledTask(@Nullable ScheduledTask task) {

if (task != null) {

this.scheduledTasks.add(task);

}

}

// 启动任务核心方法

public ScheduledTask scheduleCronTask(CronTask task) {

ScheduledTask scheduledTask = this.unresolvedTasks.remove(task);

boolean newTask = false;

if (scheduledTask == null) {

scheduledTask = new ScheduledTask(task);

newTask = true;

}

if (this.taskScheduler != null) {

scheduledTask.future = this.taskScheduler.schedule(task.getRunnable(), task.getTrigger());

} else {

addCronTask(task);

this.unresolvedTasks.put(task, scheduledTask);

}

return (newTask ? scheduledTask : null);

}

```

下面改动主要涉及到线程池数量、新增任务、删除任务、销毁任务四个方面,这些改动都是针对DynamicScheduledTaskRegistrar的。

1. 线程池数量:动态调整线程池的大小,以适应不同的任务需求。当任务数量增加时,线程池会自动扩容;当任务数量减少时,线程池会自动缩容。这样可以确保线程池始终能够高效地运行任务。

2. 新增任务:在DynamicScheduledTaskRegistrar中添加了一个新的方法,用于向线程池中添加新的任务。用户可以通过这个方法轻松地将新任务添加到线程池中,而无需担心线程池的初始化和配置问题。

3. 删除任务:在DynamicScheduledTaskRegistrar中添加了一个新的方法,用于从线程池中删除任务。用户可以通过这个方法轻松地从线程池中删除指定的任务,而无需担心线程池中的其他任务受到影响。

4. 销毁任务:在DynamicScheduledTaskRegistrar中添加了一个新的方法,用于销毁线程池中的任务。用户可以通过这个方法轻松地销毁线程池中的指定任务,而无需担心线程池中的其他任务受到影响。

总之,通过对DynamicScheduledTaskRegistrar进行上述改动,我们可以更好地管理和控制线程池中的任务执行,提高系统的性能和稳定性。

```java

public class DynamicScheduledTaskRegistrar extends ScheduledTaskRegistrar {

private static final Logger log = LoggerFactory.getLogger(DynamicScheduledTaskRegistrar.class);

private final Map scheduledTaskMap = new LinkedHashMap<>(16);

public DynamicScheduledTaskRegistrar() {

super();

// 两种实现方案

// ScheduledExecutorService scheduledExecutorService = Executors.newScheduledThreadPool(10);

// TaskScheduler taskScheduler = new ConcurrentTaskScheduler(scheduledExecutorService);

// 第二种实现方案

ThreadPoolTaskScheduler taskScheduler = new ThreadPoolTaskScheduler();

taskScheduler.setPoolSize(8);

taskScheduler.setRemoveOnCancelPolicy(true);

taskScheduler.setThreadNamePrefix("dynamic-scheduled-task-");

taskScheduler.initialize();

this.setScheduler(taskScheduler);

}

/**

* 新增任务

* @param taskName

* @param cron

* @param runnable

*/

public Boolean addCronTask(String taskName, String cron, Runnable runnable) {

if (scheduledTaskMap.containsKey(taskName)) {

log.error("定时任务[" + taskName + "]已存在,添加失败");

return Boolean.FALSE;

}

CronTask cronTask = new CronTask(runnable, cron);

ScheduledTask scheduledTask = this.scheduleCronTask(cronTask);

scheduledTaskMap.put(taskName, scheduledTask);

log.info("定时任务[" + taskName + "]新增成功");

return Boolean.TRUE;

}

/**

* 删除任务

* @param taskName

*/

public void cancelCronTask(String taskName) {

ScheduledTask scheduledTask = scheduledTaskMap.get(taskName);

if (null != scheduledTask) {

scheduledTask.cancel();

scheduledTaskMap.remove(taskName);

}

log.info("定时任务[" + taskName + "]删除成功");

}

@Override

public void destroy() {

super.destroy();

scheduledTaskMap.values().forEach(ScheduledTask::cancel);

}

}

```

线程池数量问题

在使用DynamicScheduledTaskService时,可能会遇到线程池数量不足的问题。默认情况下,线程池的数量是单线程的,这意味着如果某个任务阻塞时间过长,那么后续的任务将不得不等待,从而导致整个系统的性能下降。为了避免这种情况,我们可以采取以下几种策略:

1. 尽量使用异步任务:通过使用异步任务,我们可以将耗时的操作放到后台线程中执行,从而避免阻塞主线程。这样一来,即使某个任务的执行时间较长,也不会影响到其他任务的执行。

2. 增加线程池的数量:为了提高系统的并发性能,我们可以尝试增加线程池的数量。这样,当有大量的任务需要执行时,线程池可以同时处理更多的任务,从而提高整体的处理速度。需要注意的是,增加线程池的数量可能会带来一定的系统开销,因此需要根据实际需求进行权衡。

总之,为了避免线程池数量不足导致的问题,我们需要根据实际情况选择合适的策略来调整线程池的设置。

```java

import orgslf4j.Logger;

import org.slf4j.LoggerFactory;

import org.springframework.scheduling.annotation.Scheduled;

import org.springframework.stereotype.Service;

@Service

public class DynamicScheduledTaskService {

private static final Logger log = LoggerFactory.getLogger(DynamicScheduledTaskService.class);

private final DynamicScheduledTaskRegistrar dynamicScheduledTaskRegistrar = new DynamicScheduledTaskRegistrar();

/**

* 新增任务

* @param taskName 任务名称

* @param cron cron表达式

*/

public void add(String taskName, String cron) {

Boolean result = dynamicScheduledTaskRegistrar.addCronTask(taskName, cron, () -> print(taskName));

log.info("定时任务添加结果:" + result);

}

/**

* 取消任务

* @param taskName 任务名称

*/

public void cancel(String taskName) {

dynamicScheduledTaskRegistrar.cancelCronTask(taskName);

}

/**

* 打印任务执行日志

* @param taskName 任务名称

*/

private void print(String taskName) {

log.info(taskName + "开始");

try {

Thread.sleep(9000L);

log.info(taskName + "结束111");

} catch (Exception ex) {

}

log.info(taskName + "结束");

}

}

```

以下是重构后的代码:

```java

@RestController

@RequestMapping(value = "scheduler")

public class SchedulerController {

@Autowired

private DynamicScheduledTaskService dynamicScheduledTaskService;

/**

* 新增任务

* @param taskName 任务名称

* @param cron 定时表达式

* @return 操作结果

*/

@GetMapping(value = "add")

public Object add(@RequestParam String taskName, @RequestParam String cron) {

dynamicScheduledTaskService.add(taskName, cron);

return "SUCCESS";

}

/**

* 删除任务

* @param jobName 任务名称

* @return 操作结果

*/

@GetMapping(value = "cancel")

public Object cancel(@RequestParam String jobName) {

dynamicScheduledTaskService.cancel(jobName);

return "SUCCESS";

}

}

```