目录
概念
Spring框架为异步执行和调度任务提供了抽象对象TaskExecutor和TaskScheduler接口,Spring 同时也提供了这些接口的实现
执行器TaskExecutor
执行器是一个线程池概念的名称,执行器”命名是因为不能保证底层实现实际上是一个池。执行器可以是单线程的,甚至是同步的
最初,它存在的主要原因是当线程池使用,该接口具有单一方法(execute(Runnable task))接受基于线程池的语义和配置执行的任务。
这任务执行者最初创建它是为了给其他Spring组件提供一个线程池的抽象组件,
执行器类型
Spring包含了以下TaskExecutor的实现:
1>SyncTaskExecutor
同步任务执行器:同步的执行任务,任务的执行是在主线程中,不会启动新的线程来执行提交的任务。主要使用在没有必要使用多线程的情况,如较为简单的测试用例。
2>SimpleAsyncTaskExecutor
线程不会重用任何线程,每次调用时都会重新启动一个新的线程;但它有一个最大同时执行的线程数的限制;
3> ConcurrentTaskExecutor
并发任务处理器:它用于适配java.util.concurrent.Executor, 一般情况下请使用ThreadPoolTaskExecutor,如果hreadPoolTaskExecutor不够灵活时可以考虑采用ConcurrentTaskExecutor。
4>ThreadPoolTaskExecutor
线程池任务处理器:它是最经常使用的一个,提供了一些Bean属性用于配置java.util.concurrent.ThreadPoolExecutor并且将其包装到TaskExecutor对象中。如果需要适配java.util.concurrent.Executor,请使用ConcurrentTaskExecutor。
5>WorkManagerTaskExecutor
它实现了CommonJ中的WorkManager接口,是在Spring中使用CommonJ的WorkManager时的核心类。
6>DefaultManagedTaskExcutor
通用的工作管理器
执行器应用
如何使用
源码
public void execute(Runnable task) {
ThreadPoolExecutor executor = this.getThreadPoolExecutor();
try {
executor.execute(task);
} catch (RejectedExecutionException var4) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, var4);
}
}
public Future<?> submit(Runnable task) {
ThreadPoolExecutor executor = this.getThreadPoolExecutor();
try {
return executor.submit(task);
} catch (RejectedExecutionException var4) {
throw new TaskRejectedException("Executor [" + executor + "] did not accept task: " + task, var4);
}
}
ThreadPoolTaskExecutor
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(25);
taskExecutor.setKeepAliveSeconds(300);
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.setThreadNamePrefix("ConfTaskExcutor:");
taskExecutor.initialize();
xml config
<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
<property name="corePoolSize" value="5"/> <!--核心线程数 -->
<property name="maxPoolSize" value="10"/> <!--最大线程数 -->
<property name="keepAliveSeconds" value ="3000"/> <!--线程最大空闲时间 -->
<property name="queueCapacity" value="25"/> <!-- 队列大小 -->
<property name="threadNamePrefix" value="my-pool-"/>
<property name="rejectedExecutionHandler">
<bean class="java.util.concurrent.ThreadPoolExecutor.CallerRunsPolicy"/>
</property>
</bean>
<!-- 调用者-->
<bean id="taskExecutorExample" class="com.spring5.taskexcutor.example.TaskExecutorExample">
<constructor-arg ref="taskExecutor"/>
</bean>
public class TaskExecutorExample {
private class MessagePrinterTask implements Runnable {
private String message;
public MessagePrinterTask(String message) {
this.message = message;
}
public void run() {
System.out.println(message);
}
}
private TaskExecutor taskExecutor;
public TaskExecutorExample(TaskExecutor taskExecutor) {
this.taskExecutor = taskExecutor;
}
public void printMessages() {
for(int i = 0; i < 25; i++) {
taskExecutor.execute(new MessagePrinterTask("Message" + i));
}
}
public static void main(String[] args) {
ApplicationContext context = new ClassPathXmlApplicationContext(new String[]{"spring.xml"});
TaskExecutorExample taskExecutorExample = (TaskExecutorExample)context.getBean("taskExecutorExample");
taskExecutorExample.printMessages();
}
}
Springboot自定义
public class TaskExcutorConfig implements AsyncConfigurer {
//spring 通过 TaskExecutor 实现多线程和并发编程
//通过@EnableAsyn开启异步任务支持
@Override
public Executor getAsyncExecutor() {
ThreadPoolTaskExecutor taskExecutor = new ThreadPoolTaskExecutor();
taskExecutor.setCorePoolSize(5);
taskExecutor.setMaxPoolSize(10);
taskExecutor.setQueueCapacity(25);
taskExecutor.setKeepAliveSeconds(300);
taskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
taskExecutor.setThreadNamePrefix("ConfTaskExcutor:");
taskExecutor.initialize();
return taskExecutor;
}
public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
return null;
}
}
ThreadPoolTaskExecutor是InitializingBean、DisposableBean的实现类,
spring容器后会自动处理其初始化方法和注销方法,我们只需配置bean即可
处理流程
- 当一个任务被提交到线程池时,首先查看线程池的核心线程是否都在执行任务,否就选择一条线程执行任务,是就执行第二步。
- 查看核心线程池是否已满,不满就创建一条线程执行任务,否则执行第三步。
- 查看任务队列是否已满,不满就将任务存储在任务队列中,否则执行第四步。
- 查看线程池是否已满,不满就创建一条线程执行任务,否则就按照策略处理无法执行的任务。
注意: 当线程池满了,等待队列也满了的时候线程池会拒绝新提交的任务
决绝策略
1) AbortPolicy,线程池的默认策略,丢掉这个任务,直接抛出RejectedExecutionException让显示的你感知任务被觉得。这是默认规则
public static class AbortPolicy implements RejectedExecutionHandler {
/**
* Creates an {@code AbortPolicy}.
*/
public AbortPolicy() { }
/**
* Always throws RejectedExecutionException.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
* @throws RejectedExecutionException always
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
throw new RejectedExecutionException("Task " + r.toString() +
" rejected from " +
e.toString());
}
}
//源码:
//创建时候的默认策略
public abstract class ExecutorConfigurationSupport extends CustomizableThreadFactory implements BeanNameAware, InitializingBean, DisposableBean {
protected final Log logger = LogFactory.getLog(this.getClass());
private ThreadFactory threadFactory = this;
private boolean threadNamePrefixSet = false;
private RejectedExecutionHandler rejectedExecutionHandler = new AbortPolicy();
private boolean waitForTasksToCompleteOnShutdown = false;
private int awaitTerminationSeconds = 0;
private String beanName;
private ExecutorService executor;
....
....
}
2) CallerRunsPolicy,直接在主线程中执行,谁调用,谁执行。这样任务不会被丢失,但是会占用主线程的资源,从另一方面来说, 主线程执行任务限制了主线程提交新任务,减缓了任务提交速度。增大了子线程执行窗口期,给线程池一定的缓冲,
public static class CallerRunsPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code CallerRunsPolicy}.
*/
public CallerRunsPolicy() { }
/**
* Executes task r in the caller's thread, unless the executor
* has been shut down, in which case the task is discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
r.run();
}
}
}
3) DiscardOldestPolicy 抛弃队列头的任务(存活时间最长的任务)不会抛出异常,然后重试execute。因此这种策略会造成任务丢失
public static class DiscardOldestPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardOldestPolicy} for the given executor.
*/
public DiscardOldestPolicy() { }
/**
* Obtains and ignores the next task that the executor
* would otherwise execute, if one is immediately available,
* and then retries execution of task r, unless the executor
* is shut down, in which case task r is instead discarded.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
if (!e.isShutdown()) {
e.getQueue().poll();
e.execute(r);
}
}
}
4) DiscardPolicy策略,不做任何处理,直接丢掉这个任务,不会有异常抛出,因此这种策略会造成任务丢失
/**
* A handler for rejected tasks that silently discards the
* rejected task.
*/
public static class DiscardPolicy implements RejectedExecutionHandler {
/**
* Creates a {@code DiscardPolicy}.
*/
public DiscardPolicy() { }
/**
* Does nothing, which has the effect of discarding task r.
*
* @param r the runnable task requested to be executed
* @param e the executor attempting to execute this task
*/
public void rejectedExecution(Runnable r, ThreadPoolExecutor e) {
}
}
关闭线程池
调用shutdown或者shutdownNow,两者都不会接受新的任务,而且通过调用要停止线程的interrupt方法来中断线程,有可能线程永远不会被中断,不同之处在于shutdownNow会首先将线程池的状态设置为STOP,然后尝试停止所有线程(有可能导致部分任务没有执行完)然后返回未执行任务的列表。而shutdown则只是将线程池的状态设置为shutdown,然后中断所有没有执行任务的线程,并将剩余的任务执行完。
配置线程个数
如果是CPU密集型任务,那么线程池的线程个数应该尽量少一些,一般为CPU的个数+1条线程。
如果是IO密集型任务,那么线程池的线程可以放的很大,如2*CPU的个数。
对于混合型任务,如果可以拆分的话,通过拆分成CPU密集型和IO密集型两种来提高执行效率;如果不能拆分的的话就可以根据实际情况来调整线程池中线程的个数。
@Async 异步注释
可以提供@Async异步方法上的注释,以便异步调用该方法。换句话说,调用者立即返回位置,而方法的实际执行发生在已经提交给Spring的任务中TaskExcutor。
在最简单的情况下,您可以将注释应用于返回空的,如下例所示:
@Async
void doSomething() {
// this will be executed asynchronously
}
@Async
void doSomething(String s) {
// this will be executed asynchronously
}
即使是返回值的方法也可以异步调用。但是,这种方法需要有一个Future-类型化返回值,以便调用者可以在调用之前执行其他任务获取()在那上面将来的。以下示例显示了如何使用get()获取返回值:
@Async
Future<String> returnSomething(int i) {
// this will be executed asynchronously
}
@Async methods may not only declare a regular java.util.concurrent.Future return type but also 'Spring’s org.springframework.util.concurrent.ListenableFuture or, as of Spring 4.2, JDK 8’s java.util.concurrent.CompletableFuture, for richer interaction with the asynchronous task and for immediate composition with further processing steps.
当使用@Async时候,默认会选择1个可以异步执行的执行者, 当你需要特定选择某个执行器来执行异步方法的时候, 可以使用带有执行者的异步方法注释,例子中otherExecutor 可以是Spring容器中任何的一个Excutor Bean
@Async("otherExecutor")
void doSomething(String s) {
// this will be executed asynchronously by "otherExecutor"
}
@Async 异常处理
public class MyAsyncUncaughtExceptionHandler implements AsyncUncaughtExceptionHandler {
@Override
public void handleUncaughtException(Throwable ex, Method method, Object... params) {
// handle exception
}
}