线程池ThreadPoolTaskExecutor实战

1. ThreadPoolTaskExecutor是什么

ThreadPoolTaskExecutor是Spring基于java本身的线程池ThreadPoolExecutor做的二次封装,主要目的还是为了更加方便的在spring框架体系中使用线程池。

2. 为什么使用ThreadPoolTaskExecutor

一句话:可以让使用线程池的编码更方便更优雅。

使用线程池时,主要有两种方式:ThreadPoolExecutor和ThreadPoolTaskExecutor;

2.1直接使用ThreadPoolExecutor

原始的使用线程池,要么直接new ThreadPoolExecutor,要么使用jdk中的Executors工具类,此工具类提供了一些常用的api来创建线程池。
但是,都需要在业务代码中显式的编程,代码不够简洁。

2.2 使用ThreadPoolTaskExecutor

目前大家都在使用spring全家桶编写服务代码。也都习惯了使用注解式编程方式。ThreadPoolTaskExecutor本身相当于一个普通的bean,他只是基于ThreadPoolExecutor做了一定的封装,线程池的核心逻辑还是ThreadPoolExecutor来实现的,一些线程池的基本配置参数,也和原始的ThreadPoolExecutor的保持一致。

下面来直接通过使用demo来直观的感受下。

3. 如何使用ThreadPoolTaskExecutor

ThreadPoolTaskExecutor在spring体系中,就是一个普通的bean,所以支持bean的各种配置方式,本文demo中,就直接介绍最常用的java配置类方式。

3.1 配置

直接写一个配置类即可:

@EnableAsync
@Configuration
public class TaskConfiguration {
    @Bean("taskExecutor")
    public Executor schedulingTaskExecutor() {
        return initExecutor(4, 4, 1, new ThreadPoolExecutor.AbortPolicy(), "taskExecutor-");
    }

    private Executor initExecutor(int corePoolSize, int maxPoolSize, int queueCapacity, RejectedExecutionHandler rejectedExecutionHandler, String threadNamePrefix) {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(60);
        executor.setWaitForTasksToCompleteOnShutdown(true);
        executor.setAwaitTerminationSeconds(60);
        executor.setThreadNamePrefix(threadNamePrefix);
        executor.setRejectedExecutionHandler(rejectedExecutionHandler);
        executor.setTaskDecorator(new ContextCopyingDecorator());
        return executor;
    }
}

上面我们配置了一个线程池,名字是"taskExecutor";

参数和ThreadPoolExecutor基本一致,其中executor.setTaskDecorator(new ContextCopyingDecorator());可以是非必须的,可以不设置,具体作用在下文中介绍。

另外:@EnableAsync是spring的注解,表示开启异步代理。

当然,如果需要多个线程池,不同的业务使用不同的线程池,那么只需要在这里配置多个线程池即可,名字区分开即可。

3.2 业务中使用

上文配置类中,我们配置了一个线程池,bean的名字是“taskExecutor”,业务中就可以使用此名字进行注入。

 @Async("taskExecutor")
    @Override
    public void testMethod() {
    		//业务代码
    		....
    		//
    }

这是一个普通的接口的实现方法;

这里我们配合@Async注解使用,参数就是我们配置的线程池的名字。这样testMethod()在执行的时候,就自动使用了线程池。

在需要使用线程池的地方,只需要在方法上加上@Async(“taskExecutor”)即可。

需要注意的是,异步的方法必须使用接口的方式调用,同一个类的内部方法之间调用会失效。这个道理和spring的事务注解@Transactional一样的,不过多解释,注意到就好。

3.3 TaskDecorator

上文中的配置线程池参数时,有这么一行配置:
executor.setTaskDecorator(new ContextCopyingDecorator());
参数ContextCopyingDecorator是我们自定义的类,是一个任务装饰类,实现了接口TaskDecorator。

什么作用呢?通过一个场景来简单说明:
一个常用的业务场景是我们经常使用ThreadLocal,但是线程池是新开的线程,这就导致ThreadLocal中保存的参数失效,也就是在使用了线程池的代码中无法取到主线程中设置的ThreadLocal参数。
这个时候,就需要对ThreadLocal中参数在多个线程中传递,那么就使用到了TaskDecorator这个参数,如下:

@Slf4j
public class ContextCopyingDecorator implements TaskDecorator {

    @Override
    public Runnable decorate(Runnable runnable) {
    	//获取主线程中设置的 user对象
        Object user = ThreadLocalUtil.get(ThreadLocalUtil.USER_KEY).orElse(null);
        return () -> {
            try {
                //把user对象重新copy传递给子线程
                if (user != null) {
                    ThreadLocalUtil.set(ThreadLocalUtil.USER_KEY, user);
                }
                runnable.run();
            } finally {
                ThreadLocalUtil.remove();
            }
        };
    }
}

public class ThreadLocalUtil {
    public static final String USER_KEY = "user";

    private static final ThreadLocal<Map <String, Object>> THREAD_LOCAL = ThreadLocal.withInitial(() -> new HashMap <>(4));
    public static Optional<Object> get(String key) {
        return Optional.ofNullable(THREAD_LOCAL.get().get(key));
    }
    public static void set(String key, Object obj) {
        THREAD_LOCAL.get().put(key, obj);
    }
    public static void remove() {
        THREAD_LOCAL.remove();
    }
}

通过ContextCopyingDecorator实现了接口TaskDecorator,在Runnable decorate(Runnable runnable)方法中,实现了Threadlocal线程本地变量在线程间的传递。

其中,ContextCopyingDecorator中的finally必须得加,这里是子线程执行完毕后,清理threadlocal,防止内存泄漏。

以上就是ThreadPoolTaskExecutor最常用的使用方式,一个配置类加几个注解就可以方便的使用线程池,是不是感觉很优雅、、

  • 10
    点赞
  • 69
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值