SpringBoot中ThreadPoolTaskExecutor的使用

1 配置自己的线程池

@Configuration
@EnableAsync
public class ThreadPoolConfig
{
	// 核心线程池大小
    private int corePoolSize = 50;

    // 最大可创建的线程数
    private int maxPoolSize = 200;

    // 队列最大长度
    private int queueCapacity = 1000;

    // 线程池维护线程所允许的空闲时间
    private int keepAliveSeconds = 300;

    @Bean(name = "threadPoolTaskExecutor")
    public ThreadPoolTaskExecutor threadPoolTaskExecutor()
    {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        // 设置最大线程池数量
        executor.setMaxPoolSize(maxPoolSize);
        // 核心线程数
        executor.setCorePoolSize(corePoolSize);
        // 线程池维护线程所允许的空闲时间
        executor.setKeepAliveSeconds(keepAliveSeconds);
        // 队列最大长度
        executor.setQueueCapacity(queueCapacity);
        // 线程前缀名称
        executor.setThreadNamePrefix("ThreadPoolTaskExecutor======>");
        // 设置线程池关闭时,等待所有任务都完成后,再继续销毁其他的bean
        // 这样这些异步任务的销毁就会优先于 数据库连接池对象的销毁
        // 也就是说当其他bean都完成任务销毁后,数据库连接池对象才会销毁
        executor.setWaitForTasksToCompleteOnShutdown(true);
        // 任务的等待时间,如果超过这个时间还没有销毁,就强制销毁,以确保应用最后能够被关闭,而不是阻塞住
        executor.setAwaitTerminationSeconds(5);
        // 线程池对拒绝任务(无线程可用)的处理策略
        // 线程不够用时由调用的线程处理该任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        executor.initialize();
        return executor;
    }
}
  • 一定要自己配置,不要使用SpringBoot自带的,Spring默认的线程池没有上限,并发多时,可能会OOM。并且不是线程重用,每次都会新建一个线程。ThreadPoolTaskExecutor 会重用线程,节省创建线程的资源。因此,在使用ThreadLocal时,也要及时清理缓存,否则,会获取到上一个任务的参数。

2 使用

2.1 在Service层使用

  • Controller层直接返回结果,具体的业务,由其他线程执行。
	@Override
    @Async("threadPoolTaskExecutor")
    public void concatAB() {
    	......
    }

2.2 多线程中使用事务的写法

  • 事务放到其他方法@Transactional,然后调用。
	@Override
    @Async("threadPoolTaskExecutor")
    public void concatAB() {
    	addChange()
    	......
    }

 @Transactional(rollbackFor = Exception.class,isolation = Isolation.SERIALIZABLE)
    public void addChange(){
		.......
	}

2.3 方法内多线程

2.3.1 错误写法

  • 错误写法
	@Override
    public void concatAB() {
    	addChange()
    	......
    }

    @Async("threadPoolTaskExecutor")
    public void addChange(){
		.......
	}
  • @Async注解可以让Spring使用线程池来异步执行带有该注解的方法,当一个带有@Async注解的方法被调用时,Spring会为其创建代理对象。通过这个代理对象来调用该方法,Spring就可以控制方法的异步执行了。

  • 如果不是通过代理对象来调用@Async方法,方法的执行将与当前线程同步运行,不会实现异步处理的效果。所以在同一个类中调用加了@Async注解的方法,需要使用代理对象来调用以确保异步生效。

2.3.2 正确写法(一)

  • 第一种,直接使用线程池执行异步方法。
	@Autowired
    private ThreadPoolTaskExecutor threadPoolTaskExecutor;
	
	@Override
    public void concatAB() {
    	threadPoolTaskExecutor.execute(() -> {
            ....
        });
    }

2.3.2 正确写法(二)

  • 使用代理对象
@Override
    public void concatAB() {
        // 获取代理对象
    	IDemoService demoService = (IDemoService) AopContext.currentProxy();
    	// 使用代理对象调用
    	demoService.addChange()
    	......
    }

    @Async("threadPoolTaskExecutor")
    public void addChange(){
		.......
	}

2.3.3 正确写法(三)

  • DemoService中通过@Autowired将自身注入到selfProxy属性中,并使用selfProxy对象在syncMethod方法中调用了asyncMethod方法,从而实现了对同一类中的带有@Async注解的方法的异步调用。
@Autowired
    private DemoService selfProxy;

    @Async
    public void asyncMethod() {
        // 异步执行的逻辑
    }

    public void syncMethod() {
        // 调用异步方法需要使用代理对象
        selfProxy.asyncMethod();
    }

3 线程池与并行流的选取

  • IO密集型的业务,选取线程池来完成。(例如,连接数据库之类的操作)
  • cpu密集型的业务,选取并行流allelesvs.parallelStream()的方式处理。(例如,计算类、过滤、求和、分组等)
  • 1
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逻辑峰

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值