spring异步线程任务Async,自定义配置线程池,Java

Posted zhangphil

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了spring异步线程任务Async,自定义配置线程池,Java相关的知识,希望对你有一定的参考价值。

spring异步线程任务Async,自定义配置线程池,Java

(一)spring里面,可以通过简洁的@Async使用默认的线程池跑多线程任务。

package zhangphil.demo;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.context.ConfigurableApplicationContext;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class TestApplication 
    public static void main(String[] args) 
        ConfigurableApplicationContext context=SpringApplication.run(TestApplication.class, args);
    

主Application,需要配置注解 @EnableAsync 启动异步线程任务。

package zhangphil.demo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class MyTask 
    private int a = 0;
    private int b = 0;
    private int c = 0;

    @Async
    public void task1() 
        log.info("t-a ...");
        try 
            Thread.sleep(5000);
         catch (InterruptedException e) 
            throw new RuntimeException(e);
        
        log.info("t-a-", a++);
    

    @Async
    public void task2() 
        log.info("t-b ...");
        try 
            Thread.sleep(5000);
         catch (InterruptedException e) 
            throw new RuntimeException(e);
        
        log.info("t-b-", b++);
    

    @Async
    public void task3() 
        log.info("t-c ...");
        try 
            Thread.sleep(5000);
         catch (InterruptedException e) 
            throw new RuntimeException(e);
        
        log.info("t-c-", c++);
    

上面就是异步线程任务的主要模块。

下面做一个Controller,没别的功能,只为在浏览器输入localhost:8080/start启动task1(),task2(),task()3:

package zhangphil.demo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.bind.annotation.RestController;

@RestController
@Slf4j
public class MyController 
    @Autowired
    private MyTask myTask;

    @RequestMapping(value = "/start", method = RequestMethod.GET)
    private String start() 
        myTask.task1();
        myTask.task2();
        myTask.task3();
        log.info("start ok");
        return "OK";
    

启动后,在浏览器访问localhost:8080/start后,后台输出:

 spring默认的为多线程任务装载了线程名为 task-1,task-2,task-3 启动它们。以上代码我们并没有主动创建线程池,而是spring默默的用框架包装好的ThreadPoolTaskExecutor为我们的代码启动了多线程。

(二)自定义配置Async的线程池。

以上是spring原生的多线程异步任务框架,我们没有添加任何配置参数,只是简单的调用。如果要自定义更细颗粒度的使用和理解线程池任务,可以通过配置@Configuration配置AsyncConfigurer

实现对spring框架默认的ThreadPoolTaskExecutor二次定制化配置,配置代码:

package zhangphil.demo;

import org.springframework.aop.interceptor.AsyncUncaughtExceptionHandler;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.AsyncConfigurer;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;
import java.util.concurrent.ThreadPoolExecutor;

@Configuration
public class MyAsyncConfigurer implements AsyncConfigurer 

    @Override
    public Executor getAsyncExecutor() 
        return executor();
    

    @Bean("myTaskExecutor")
    public ThreadPoolTaskExecutor executor() 
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        int corePoolSize = 10;
        executor.setCorePoolSize(corePoolSize);

        int maxPoolSize = 50;
        executor.setMaxPoolSize(maxPoolSize);

        int queueCapacity = 10;
        executor.setQueueCapacity(queueCapacity);

        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        String threadNamePrefix = "my-task@";
        executor.setThreadNamePrefix(threadNamePrefix);

        //executor.setAwaitTerminationMillis(100*1000);
        //executor.setWaitForTasksToCompleteOnShutdown(true);
        //executor.setAllowCoreThreadTimeOut(true);

        executor.setKeepAliveSeconds(60 * 1000);

        executor.initialize();

        return executor;
    

    @Override
    public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() 
        return AsyncConfigurer.super.getAsyncUncaughtExceptionHandler();
    

MyTask.java不做任何修改,运行后输出:

 可以看到,此时的线程任务名称已经变为我们自定义的my-task@ 开头。

(三)配置多个线程池。

有些项目中的线程池可能不止一个,而是三个甚至更多,那就不太适合通过(二)中的技术路线实现了,而需要更一般、更通用的方法实现。

package zhangphil.demo;

import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.ThreadPoolExecutor;

@Configuration
public class MyTaskExecutor 

    @Bean("executorA")
    public ThreadPoolTaskExecutor executorA() 
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        int corePoolSize = 10;
        executor.setCorePoolSize(corePoolSize);

        int maxPoolSize = 50;
        executor.setMaxPoolSize(maxPoolSize);

        int queueCapacity = 10;
        executor.setQueueCapacity(queueCapacity);

        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        String threadNamePrefix = "task@A-";
        executor.setThreadNamePrefix(threadNamePrefix);

        //executor.setAwaitTerminationMillis(100*1000);
        //executor.setWaitForTasksToCompleteOnShutdown(true);
        //executor.setAllowCoreThreadTimeOut(true);

        executor.setKeepAliveSeconds(60 * 1000);

        executor.initialize();

        return executor;
    

    @Bean("executorB")
    public ThreadPoolTaskExecutor executorB() 
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();

        int corePoolSize = 10;
        executor.setCorePoolSize(corePoolSize);

        int maxPoolSize = 50;
        executor.setMaxPoolSize(maxPoolSize);

        int queueCapacity = 10;
        executor.setQueueCapacity(queueCapacity);

        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());

        String threadNamePrefix = "task@B-";
        executor.setThreadNamePrefix(threadNamePrefix);

        //executor.setAwaitTerminationMillis(100*1000);
        //executor.setWaitForTasksToCompleteOnShutdown(true);
        //executor.setAllowCoreThreadTimeOut(true);

        executor.setKeepAliveSeconds(60 * 1000);

        executor.initialize();

        return executor;
    


上面代码通过@Configuration定义了不同于spring框架默认的那个多线程池子,这两个单独自定义的线程池名分别以task@A-和task@B-开头。多线程池实现了,对应的使用这两个线程池的任务代码模块也需要调整:

package zhangphil.demo;

import lombok.extern.slf4j.Slf4j;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;

@Slf4j
@Component
public class MyTask 
    private int a = 0;
    private int b = 0;
    private int c = 0;

    @Async("executorA")
    public void task1() 
        log.info("t-a ...");
        try 
            Thread.sleep(5000);
         catch (InterruptedException e) 
            throw new RuntimeException(e);
        
        log.info("t-a-", a++);
    

    @Async
    public void task2() 
        log.info("t-b ...");
        try 
            Thread.sleep(5000);
         catch (InterruptedException e) 
            throw new RuntimeException(e);
        
        log.info("t-b-", b++);
    

    @Async("executorB")
    public void task3() 
        log.info("t-c ...");
        try 
            Thread.sleep(5000);
         catch (InterruptedException e) 
            throw new RuntimeException(e);
        
        log.info("t-c-", c++);
    

注意task2(),特意没有给@Async加上寻址名字。看看后台运行输出:

 从输出可以看到,由于在task1()和task3()指定了@Async()需要启用的线程池executorA和executorB,所以task1()和task3()的多线程任务分别被executorA和executorB调度装载运行。我们故意没有给task2()的@Async指定线程池,但由于在Application里面通过配置注解@EnableAsync

开启了多线程任务特性,所以,spring框架为task2()也启用了默认的未经配置的线程池。

以上是关于spring异步线程任务Async,自定义配置线程池,Java的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 自定义线程池使用@Async实现异步调用任务

Spring Boot使用@Async实现异步调用:自定义线程池

Spring Boot中有多个@Async异步任务时,记得做好线程池的隔离!

Spring Boot中有多个@Async异步任务时,记得做好线程池的隔离!

Java高并发编程实战5,异步注解@Async自定义线程池

Java高并发编程实战5,异步注解@Async自定义线程池