Spring线程池简介及应用整理

本文详细介绍了Spring线程池,包括TaskExecutor接口、SimpleAsyncTaskExecutor、SyncTaskExecutor和ThreadPoolTaskExecutor的使用。着重讨论了ThreadPoolTaskExecutor的配置与线程池的工作原理,以及在SpringBoot2.x中的整合应用,包括自定义线程池、异常处理和拒绝策略。

Spring线程池

简单介绍

Spring的TaskExecutor接口等同于java.util.concurrent.Executor接口。 实际上,它存在的主要原因是为了在使用线程池的时候,将对Java 5的依赖抽象出来。 这个接口只有一个方法execute(Runnable task),它根据线程池的语义和配置,来接受一个执行任务。

最初创建TaskExecutor是为了在需要时给其他Spring组件提供一个线程池的抽象。 例如ApplicationEventMulticaster组件、JMS的 AbstractMessageListenerContainer和对Quartz的整合都使用了TaskExecutor抽象来提供线程池。 当然,如果你的bean需要线程池行为,你也可以使用这个抽象层。

TaskExecutor类型

SimpleAsyncTaskExecutor 这个实现不重用任何线程,或者说它每次调用都启动一个新线程。但是,它还是支持对并发总数设限,当超过线程并发总数限制时,阻塞新的调用,直到有位置被释放。

SyncTaskExecutor 不是异步的线程.同步可以用SyncTaskExecutor,但这个可以说不算一个线程池,因为还在原线程执行。这个类没有实现异步调用,只是一个同步操作。

ConcurrentTaskExecutor Executor的适配类,不推荐使用。如果ThreadPoolTaskExecutor不满足要求时,才用考虑使用这个类。

SimpleThreadPoolTaskExecutor 监听Spring’s lifecycle callbacks,并且可以和Quartz的Component兼容.是Quartz的SimpleThreadPool的类。线程池同时被quartz和非quartz使用,才需要使用此类。

ThreadPoolTaskExecutor 最常用。要求jdk版本大于等于5。可以在程序而不是xml里修改线程池的配置.其实质是对java.util.concurrent.ThreadPoolExecutor的包装。

TimerTaskExecutor 这个实现使用一个TimerTask作为其背后的实现。它和SyncTaskExecutor的不同在于,方法调用是在一个独立的线程中进行的,虽然在那个线程中是同步的。

WorkManagerTaskExecutor 这个实现使用CommonJ WorkManager作为它的支持服务提供者,它是在Spring应用程序上下文中设置基于CommonJ的WebLogic/WebSphere线程池集成的中心便利类。

SyncTaskExecutor

  1. SyncTaskExecutor:同步可以用SyncTaskExecutor,但这个可以说不算一个线程池,因为还在原线程执行。这个类没有实现异步调用,只是一个同步操作。

  2. 也可以用ThreadPoolTaskExecutor结合FutureTask做到同步。

SyncTaskExecutor与ThreadPoolTaskExecutor区别

前者是同步执行器,执行任务同步,后者是线程池,执行任务异步。

SimpleAsyncTaskExecutor

异步执行用户任务的SimpleAsyncTaskExecutor。每次执行客户提交给它的任务时,它会启动新的线程,并允许开发者控制并发线程的上限(concurrencyLimit),从而起到一定的资源节流作用。默认时,concurrencyLimit取值为-1,即不启用资源节流。

<bean id="simpleAsyncTaskExecutor"   
    class="org.springframework.core.task.SimpleAsyncTaskExecutor">  
    <property name="daemon" value="true"/>  
    <property name="concurrencyLimit" value="2"/>  
    <property name="threadNamePrefix" value="simpleAsyncTaskExecutor"/>
</bean> 

主要实现:1.支持限流处理 2.异步注册线程返回结果

public class SimpleAsyncTaskExecutor extends CustomizableThreadCreator implements AsyncListenableTaskExecutor, Serializable {
    //限流主要实现
    private final SimpleAsyncTaskExecutor.ConcurrencyThrottleAdapter concurrencyThrottle = new SimpleAsyncTaskExecutor.ConcurrencyThrottleAdapter();
    private ThreadFactory threadFactory;
    //设置最大的线程数量
    public void setConcurrencyLimit(int concurrencyLimit) {
            this.concurrencyThrottle.setConcurrencyLimit(concurrencyLimit);
    }
    //是否开启了限流 限流数量大于0?
    public final boolean isThrottleActive() {
            return this.concurrencyThrottle.isThrottleActive();
    }
    //1.是否开启限流 否则不开启限流处理
    //2.执行开始之前检测是否可以满足要求 当前数量++
    //3.开启限流将执行的Runable进行封装,执行完成调用final方法 当前数量--
    public void execute(Runnable task, long startTimeout) {
            Assert.notNull(task, "Runnable must not be null");
            if(this.isThrottleActive() && startTimeout > 0L) {
                this.concurrencyThrottle.beforeAccess();
                this.doExecute(new SimpleAsyncTaskExecutor.ConcurrencyThrottlingRunnable(task));
            } else {
                this.doExecute(task);
            }
      }
     //异步提交有返回值
    public Future<?> submit(Runnable task) {
          FutureTask future = new FutureTask(task, (Object)null);
          this.execute(future, 9223372036854775807L);
          return future;
      }

      public <T> Future<T> submit(Callable<T> task) {
          FutureTask future = new FutureTask(task);
          this.execute(future, 9223372036854775807L);
          return future;
      }

      public ListenableFuture<?> submitListenable(Runnable task) {
          ListenableFutureTask future = new ListenableFutureTask(task, (Object)null);
          this.execute(future, 9223372036854775807L);
          return future;
      }

      public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
          ListenableFutureTask future = new ListenableFutureTask(task);
          this.execute(future, 9223372036854775807L);
          return future;
      }
      //拥有工厂?没有的话调用父类可以设置各种参数的创建线程
      protected void doExecute(Runnable task) {
          Thread thread = this.threadFactory != null?this.threadFactory.newThread(task):this.createThread(task);
          thread.start();
      }
      //父类的方法,方便配置线程,方便xml设置线程参数CustomizableThreadCreator 
      public Thread createThread(Runnable runnable) {
            Thread thread = new Thread(getThreadGroup(), runnable, nextThreadName());
            thread.setPriority(getThreadPriority());
            thread.setDaemon(isDaemon());
            return thread;
        }


 }
限流处理其实就是在执行任务之前和之后对于当前线程数量进行统计

内部类的实现

//下面只是对于操作进行简单的封装,最真的实现还是抽象的ConcurrencyThrottleSupport
  private static class ConcurrencyThrottleAdapter extends ConcurrencyThrottleSupport {
        private ConcurrencyThrottleAdapter() {
        }

        protected void beforeAccess() {
            super.beforeAccess();
        }

        protected void afterAccess() {
            super.afterAccess();
        }
    }

简单的通过synchronized和wati and notify达到控制线程数量的效果,从而实现限流的策略。
详见ConcurrencyThrottleSupport和ConcurrencyThrottleInterceptor源码

执行任务时,每次都会新建一个线程,不使用线程池
    protected void doExecute(Runnable task) {
        Thread thread = (this.threadFactory != null ? this.threadFactory.newThread(task) : createThread(task));
        thread.start();
    }

这个实现并不复用线程,如果你要复用线程请去使用线程池的实现。这个是用来执行很多耗时很短的任务的。

ListenableFutureTask

ListenableFutureTask 其实主要是依靠FutureTask这个JDK的封装,覆盖了原始的run方法,在run中封装可以获取到线程的返回值。
ListenableFutureTask 在次封装,由于FutureTask执行完成之后会调用done()空方法,ListenableFutureTask覆盖done方法可以获取到执行的结果,然后在调用前期注册的错误处理或者成功处理的方法,即可到达异步处理的效果。

public class ListenableFutureTask<T> extends FutureTask<T> implements ListenableFuture<T> {
    private final ListenableFutureCallbackRegistry<T> callbacks = new ListenableFutureCallbackRegistry();

    public ListenableFutureTask(Callable<T> callable) {
        super(callable);
    }

    public ListenableFutureTask(Runnable runnable, T result) {
        super(runnable, result);
    }

    public void addCallback(ListenableFutureCallback<? super T> callback) {
        this.callbacks.addCallback(callback);
    }

    public void addCallback(SuccessCallback<? super T> successCallback, FailureCallback failureCallback) {
        this.callbacks.addSuccessCallback(successCallback);
        this.callbacks.addFailureCallback(failureCallback);
    }
    //FutureTask执行完成后的回调,调用监听接口的实现类的方法
    protected final void done() {
        Object cause;
        try {
            Object ex = this.get();
            //回调实现类的方法
            this.callbacks.success(ex);
            return;
        } catch (InterruptedException var3) {
            Thread.currentThread().interrupt();
            return;
        } catch (ExecutionException var4) {
            cause = var4.getCause();
            if(cause == null) {
                cause = var4;
            }
        } catch (Throwable var5) {
            cause = var5;
        }

        this.callbacks.failure((Throwable)cause);
    }
}

ThreadPoolTaskExecutor

<bean id="taskExecutor" class="org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor">
  <property name="corePoolSize" value="5" />
  <property name="maxPoolSize" value="10" />
  <property name="queueCapacity" value="25" />
</bean>

<bean id="taskExecutorExample" class="TaskExecutorExample">
  <constructor-arg ref="taskExecutor" />
</bean>

配置解释
当一个任务通过execute(Runnable)方法欲添加到线程池时:
1、 如果此时线程池中的数量小于corePoolSize,即使线程池中的线程都处于空闲状态,也要创建新的线程来处理被添加的任务。
2、 如果此时线程池中的数量等于 corePoolSize,但是缓冲队列 workQueue未满,那么任务被放入缓冲队列。
3、如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量小于maximumPoolSize,建新的线程来处理被添加的任务。
4、 如果此时线程池中的数量大于corePoolSize,缓冲队列workQueue满,并且线程池中的数量等于maximumPoolSize,那么通过 handler所指定的策略来处理此任务。也就是:处理任务的优先级为:核心线程corePoolSize、任务队列workQueue、最大线程 maximumPoolSize,如果三者都满了,使用handler处理被拒绝的任务。
5、 当线程池中的线程数量大于 corePoolSize时,如果某线程空闲时间超过keepAliveTime,线程将被终止。这样,线程池可以动态的调整池中的线程数。
推荐 - 使用线程池版本

<?xml version="1.0" encoding="UTF-8"?>
<!--Spring框架的xml标签定义文档, 可访问http://www.springframework.org/schema/task/查看最新task组件的xml标签文档-->
<beans xmlns:task="http://www.springframework.org/schema/task"
       xsi:schemaLocation="
                http://www.springframework.org/schema/task
                http://www.springframework.org/schema/task/spring-task-3.2.xsd">

    <!--扫描项目实例化@Component,@Service,@Controller修饰的类-->
    <context:component-scan base-package="com.your_app" />

    <!-- 在代码中@Async不加参数就会使用task:annotation-driven标签定义的executor-->
    <task:annotation-driven executor="myExecutor"/>
    <!-- 在代码中@Async("myExecutor")可以显式指定executor为"myExecutor"-->
    <task:executor id="myExecutor"  
               pool-size="5-25"
               queue-capacity="100"
               rejection-policy="CALLER_RUNS"/>
</beans>

pool-size的值”5-25”是一个范围,这对应的是线程池的min和max容量,它们的意义请参考本文上一节的“配置说明”里的第3、4点。如果只有一个值,如pool-size=n, 意味着minSizemaxSizen
rejection-policy
AbortPolicy(默认)抛异常
DiscardPolicy or DiscardOldestPolicy放弃该线程
CallerRunsPolicy通知该线程的创建者,让其不要提交新的线程

SpringBoot2.x整合线程池(ThreadPoolTaskExecutor)

1.SpringBoot对线程池的自动装载

源代码:org.springframework.boot.autoconfigure.task.TaskExecutionAutoConfiguration

@Bean
    @ConditionalOnMissingBean
    public TaskExecutorBuilder taskExecutorBuilder() {
        TaskExecutionProperties.Pool pool = this.properties.getPool();
        TaskExecutorBuilder builder = new TaskExecutorBuilder();
        builder = builder.queueCapacity(pool.getQueueCapacity());
        builder = builder.corePoolSize(pool.getCoreSize());
        builder = builder.maxPoolSize(pool.getMaxSize());
        builder = builder.allowCoreThreadTimeOut(pool.isAllowCoreThreadTimeout());
        builder = builder.keepAlive(pool.getKeepAlive());
        builder = builder.threadNamePrefix(this.properties.getThreadNamePrefix());
        builder = builder.customizers(this.taskExecutorCustomizers);
        builder = builder.taskDecorator(this.taskDecorator.getIfUnique());
        return builder;
    }

    @Lazy
    @Bean(name = { APPLICATION_TASK_EXECUTOR_BEAN_NAME,
            AsyncAnnotationBeanPostProcessor.DEFAULT_TASK_EXECUTOR_BEAN_NAME })
    @ConditionalOnMissingBean(Executor.class)
    public ThreadPoolTaskExecutor applicationTaskExecutor(TaskExecutorBuilder builder) {
        return builder.build();
    }
自定义线程池

根据业务配置不同的线程池

@Configuration
@EnableAsync
@Slf4j
public class ExecutorConfig {
  
    @Bean
    public Executor asyncServiceExecutor() {
        ThreadPoolTaskExecutor threadPoolTaskExecutor = new VisiableThreadPoolTaskExecutor();
        //核心线程数
        threadPoolTaskExecutor.setCorePoolSize(5);
        threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
        //最大线程数
        threadPoolTaskExecutor.setMaxPoolSize(5);
        //配置队列大小
        threadPoolTaskExecutor.setQueueCapacity(50);
        //配置线程池前缀
        threadPoolTaskExecutor.setThreadNamePrefix("async-service-");
        //拒绝策略
//        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        threadPoolTaskExecutor.setRejectedExecutionHandler(new PrintingPolicy());
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }

    @Bean
    public Executor customServiceExecutor(){
        ThreadPoolTaskExecutor threadPoolTaskExecutor=new ThreadPoolTaskExecutor();
        //线程核心数目
        threadPoolTaskExecutor.setCorePoolSize(10);
        threadPoolTaskExecutor.setAllowCoreThreadTimeOut(true);
        //最大线程数
        threadPoolTaskExecutor.setMaxPoolSize(10);
        //配置队列大小
        threadPoolTaskExecutor.setQueueCapacity(50);
        //配置线程池前缀
        threadPoolTaskExecutor.setThreadNamePrefix("custom-service-");
        //配置拒绝策略
        threadPoolTaskExecutor.setRejectedExecutionHandler(new ThreadPoolExecutor.AbortPolicy());
        //数据初始化
        threadPoolTaskExecutor.initialize();
        return threadPoolTaskExecutor;
    }
}
若是想在使用连接池的时候,打印出连接池的各项参数
@Slf4j
public class VisiableThreadPoolTaskExecutor extends ThreadPoolTaskExecutor {


    //打印队列的详细信息
    private void showThreadPoolInfo(String prefix){
        ThreadPoolExecutor threadPoolExecutor = getThreadPoolExecutor();

        if(null==threadPoolExecutor){
            return;
        }

        log.info("{}, {},taskCount [{}], completedTaskCount [{}], activeCount [{}], queueSize [{}]",
                this.getThreadNamePrefix(),
                prefix,
                threadPoolExecutor.getTaskCount(),
                threadPoolExecutor.getCompletedTaskCount(),
                threadPoolExecutor.getActiveCount(),
                threadPoolExecutor.getQueue().size());
    }


    @Override
    public void execute(Runnable task) {
        showThreadPoolInfo("1. do execute");
        super.execute(task);
    }

    @Override
    public void execute(Runnable task, long startTimeout) {
        showThreadPoolInfo("2. do execute");
        super.execute(task, startTimeout);
    }

    @Override
    public Future<?> submit(Runnable task) {
        showThreadPoolInfo("1. do submit");
        return super.submit(task);
    }

    @Override
    public <T> Future<T> submit(Callable<T> task) {
        showThreadPoolInfo("2. do submit");
        return super.submit(task);
    }

    @Override
    public ListenableFuture<?> submitListenable(Runnable task) {
        showThreadPoolInfo("1. do submitListenable");
        return super.submitListenable(task);
    }

    @Override
    public <T> ListenableFuture<T> submitListenable(Callable<T> task) {
        showThreadPoolInfo("2. do submitListenable");
        return super.submitListenable(task);
    }

}
如何使用连接池

在业务方法中使用@Async注解,并且可以选择使用的连接池。来启动一个异步任务。

//带返回值的任务
    @Async("asyncServiceExecutor")
    public Future<String> doTask1() throws InterruptedException{
        log.info("Task1 started.");
        long start = System.currentTimeMillis();
        Thread.sleep(5000);
        long end = System.currentTimeMillis();

        log.info("Task1 finished, time elapsed: {} ms.", end-start);

        return new AsyncResult<>("Task1 accomplished!");
    }
//创建的是Runnable的任务
    @Async("asyncServiceExecutor")
    public void executeAsync() {
        log.info("start executeAsync");
        try{
            Thread.sleep(1000);
        }catch(Exception e){
            e.printStackTrace();
        }
        log.info("end executeAsync");
    }

如何获取任务的返回值

Runnable异常处理
@Configuration
public class ExecutorConfig implements AsyncConfigurer {
    //配置异常处理机制
    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
        return (ex,method,params)->{
            log.error("异步线程执行失败。方法:[{}],异常信息[{}] : ", method, ex.getMessage(),ex);
        };
    }
}
自定义拒绝策略
    private class CustomRejectedExecutionHandler implements RejectedExecutionHandler {

        @Override
        public void rejectedExecution(Runnable r, ThreadPoolExecutor executor) {
            try {
                //自定义
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值