1.TaskExecutor类型 该接口有一个execute(Runnable task)方法,他根据线程池的语义和配置接受执行任务
SimpleAsyncTaskExecutor 此实现不重用任何线程,他会为每个调用都启用一个新线程,但是他支持一个并发限制,他将阻止任何超过该限制的调用,直到一个插槽被释放为止。
ConcurrentTaskExecutor 此实现是 java.util.concurrent.Excutor对象的适配器,有一个可选的ThreadPoolTaskExecutor,他将执行器配置参数作为Bean属性公开,很少需要使用到ConcurrentTaskExecutor,但是如果ThreadPoolTaskExecutor不够灵活,那么你就需要ConcurrentTaskExecutor。
SimpleThreadPoolTaskExecutor 这个实现是Quartz的SimpleThreadPool的子类,监听Spring的生命周期回调,当有一个可能需要由Quartz和non-Quartz组件共享的线程池时,通常使用此方法
ThreadPoolTaskExecutor 此实现是最常见的一种,他公开配置了java.util.concurrent.ThreadPoolExecutor的bean属性,并将其包装在TaskExecutor中。
2.使用TaskExcutor
a.配置
@Bean
public TaskExecutor taskExecutor() {
ThreadPoolTaskExecutor threadPoolExecutor = new ThreadPoolTaskExecutor();
threadPoolExecutor.setCorePoolSize(5);
threadPoolExecutor.setMaxPoolSize(10);
threadPoolExecutor.setQueueCapacity(25);
return threadPoolExecutor;
}
b.编写业务逻辑
@Service
public class TaskExecutorExample {
private class MessagePrinterTask implements Runnable {
private String message;
public MessagePrinterTask(String message) {
this.message = message;
}
@Override
public void run() {
System.out.println(message);
}
}
private TaskExecutor taskExecutor;
@Autowired
public TaskExecutorExample(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public void printMessage() {
for (int i = 0; i < 25; i++) {
taskExecutor.execute(new MessagePrinterTask("Message_" + i));
}
}
}
c.编写测试类
@Autowired
private TaskExecutorExample taskExecutorExample;
@GetMapping(value = "/get2")
public Object get2() {
taskExecutorExample.printMessage();
return "null";
}
3.Spring 的 TaskExecutor抽象
除了TaskExecutor抽象之外,Spring3.0还引入了TaskSchedule,其中有各种方法来调度任务,以便在将来的某个时刻运行
public interface TaskScheduler {
@Nullable
ScheduledFuture<?> schedule(Runnable var1, Trigger var2);
default ScheduledFuture<?> schedule(Runnable task, Instant startTime) {
return this.schedule(task, Date.from(startTime));
}
ScheduledFuture<?> schedule(Runnable var1, Date var2);
default ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Instant startTime, Duration period) {
return this.scheduleAtFixedRate(task, Date.from(startTime), period.toMillis());
}
ScheduledFuture<?> scheduleAtFixedRate(Runnable var1, Date var2, long var3);
default ScheduledFuture<?> scheduleAtFixedRate(Runnable task, Duration period) {
return this.scheduleAtFixedRate(task, period.toMillis());
}
ScheduledFuture<?> scheduleAtFixedRate(Runnable var1, long var2);
default ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Instant startTime, Duration delay) {
return this.scheduleWithFixedDelay(task, Date.from(startTime), delay.toMillis());
}
ScheduledFuture<?> scheduleWithFixedDelay(Runnable var1, Date var2, long var3);
default ScheduledFuture<?> scheduleWithFixedDelay(Runnable task, Duration delay) {
return this.scheduleWithFixedDelay(task, delay.toMillis());
}
ScheduledFuture<?> scheduleWithFixedDelay(Runnable var1, long var2);
}
最简单的方法时如下的方法
ScheduledFuture<?> schedule(Runnable var1, Date var2);
只需要一个Runnable和Date,使用它任务在指定的时间只会执行一次,其它的所有方法都是可以重复执行任务计划,通过这些方法,在简单的周期中需要以固定频率和固定时间间隔方法执行任务时现实的,但是使用Trigger会跟方便。
Trigger接口
Trigger接口是JSR-236必须实现的,Spring3.0没有提供官方实现,Trigger的基本目的是执行调度,执行时间可以根据过去的执行结果或者甚至任意条件来确定,如果考虑到了前一次的执行结果,则该信息在TriggerContext中可用,触发器接口本身非常简单。
public interface Trigger {
@Nullable
Date nextExecutionTime(TriggerContext var1);
}
正如所看到的的,TriggerContext是最重要的部分,他封装了所有相关数据,并且在必要的时候开发供将来扩展,TriggerContext是一个接口,默认情况下使用SimpleTriggerContext实现
public interface TriggerContext {
@Nullable
Date lastScheduledExecutionTime();
@Nullable
Date lastActualExecutionTime();
@Nullable
Date lastCompletionTime();
}
Trigger实现
Spring提供了两个Trigger接口实现,最有趣的是一个CronTrigger,它支持基于cron表达式的任务调度
例如:
scheduler.schedule(task,new CronTrigger("0 15 9-17 * * MON-FRI"))
另一个实现是PeriodicTrigger,他接受一个固定的周期,一个可选的初始延迟值和一个布尔值来表示该期间是否应解释为固定速率或固定延迟。由于TaskScheduler接口已经定义了以固定速率或固定延迟来调度任务的方法,延迟应该尽可能直接使用这些方法。PeriodicTrigger实现的价值在于,他可以在依赖于触发器抽象的组件中使用。
对调度和异步执行的注解支持
1.启用调度注解
@EnableScheduling //开启调度注解
@EnableAsync //开启异步执行
@Scheduled 注解
可以将@Scheduled注解与触发器元元素一起添加到方法中,例如下面的方法,每隔5秒钟调用一次固定的延迟,这意味着该期间从每次调用的完成时间计算
@Scheduled(fixedDelay = 5000)
public void doSomething() {
//...
}
如果需要按照固定的速度来执行,只需要简单的改变注解中属性的属性名就行了,下面可以在每5秒开始调用执行
@Scheduled(fixedRate = 5000)
public void doSomething() {
//...
}
对于固定延迟和固定速率任务,可以指定初始延迟,指示在第一次执行该方案之前等待的毫秒数
@Scheduled(initialDelay = 1000,fixedDelay = 5000)
public void doSomething() {
//...
}
如果简单的周期幅度不能满足要求,可以使用cron表达式 可以使用zone属性来指定解析cron表达式的时区
@Scheduled(cron = "*/5 * * * * MON-FRI")
public void doSomething() {
//...
}
@Async注解
可以在方法上提供@Async注解,以便发生该方法的异步调用,换言之,调用方法将在调用时,立即返回,并且该方法的实际执行将发生在已提交到SpringTaskExecutor的任务在,在最简单的情况下,注解可以应用于void返回方法
@Async
public void doSomething() {
//...
}
与使用@Scheduled注解的注解方法不同,这些方法可以有预期的参数
即使有返回值的方法可以异步调用,但是此类方法需要具有Future类型的返回值,这仍然提供了异步执行的好处,这样就可以在将来调用get()之前执行其他任务。
@Async
public Future<String> doSomething(int i) {
//...
}
提示:@Async 方法不仅可以申明一个常规的java.util.concurrent.Future返回类型,而且还可以是org.springframework.util.concurrent.ListenableFuture 以及JDK1.8的 CompletableFuture用于与异步任务进行更丰富的交互。
@Async不能与生命周期回调(如 @PostConstruct)y一起使用,若要异步初始化Spring bean则当前必须使用单独的初始化Spring bean,然后在目标方法上调用@Async注解方法。
public class SampleBeanInitalizer {
private final SampleBean bean;
public SampleBeanInitalizer(SampleBean bean) {
this.bean = bean;
}
@PostConstruct
public void initialize() {
bean.doSomething;
}
}
public class SampleBeanImpl implements SampleBean {
@Async
void doSomething() {
}
}
使用@Aysnc的Executor的条件
默认情况下,在方法上指定@Aysnc时,将使用的执行器是提供给“annotation-driven”元素的一个 但是,当需要指示执行方法使用指定的执行器时,可以使用@Async注解的value属性
public class SampleBeanImpl implements SampleBean {
@Async("otherExecutor")
void doSomething() {
}
}
使用@Async的异常管理
当@Aynsc方法由Future类型的返回值时,可以很容器的管理在方法执行期间引发的异常,但是对于void返回类型异常是无法捕获无法传输的,对于这种情况可以使用AsyncUncaughtExceptionHandler来处理此类异常
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler{
@Override
public void handleUncaughtException(Throwable throwable, Method method, Object... objects) {
// handle exception
}
}