Springboot @Async及线程池的使用和扩展

简介: Springboot @Async及线程池的使用和扩展

 最近在看阿里的JAVA开发手册,说到 线程资源必须通过线程池提供,不允许在应用中自行显式创建线程(new Thread()这种)。

首先在springboot中,它已经给我们提供了很方便的异步和线程池机制。实现异步只要加一个注解@Async,就可以实现了

阿里的JAVA开发手册还说到:使用线程池的好处是减少在创建和销毁线程上所消耗的时间以及系统资源的开销,解决资源不足的问题。如果不使用线程池,有可能造成系统创建大量同类线程而导致消耗完内存或者“过度切换”的问题

Spring是通过任务执行器(TaskExecutor)来实现多线程和并发编程,使用ThreadPoolTaskExecutor来创建一个基于线城池的TaskExecutor

1、开启异步任务

本文springboot版本:

2.0.6.RELEASE

image.gif

在启动类上加注解

@EnableAsync

image.gif

2、配置类 TaskPoolConfig

@Configuration
public class TaskPoolConfig {
    @Bean("taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
        //最大线程数30
        executor.setPoolSize(30);
        //线程池名的前缀
        executor.setThreadNamePrefix("taskExecutor-");
        //设置线程池关闭的时候等待所有任务都完成再继续销毁其他的Bean
        executor.setWaitForTasksToCompleteOnShutdown(true);
        //设置线程池中任务的等待时间
        executor.setAwaitTerminationSeconds(60);
        //当线程池没有处理能力的时候,该策略会直接在 execute 方法的调用线程中运行被拒绝的任务;如果执行程序已关闭,则会丢弃该任务
        executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
        return executor;
    }
}

image.gif

值得留意的是  上面的方法名称为taskExecutor

这里我没有用网上更常见的ThreadPoolTaskExecutor 而是用了ThreadPoolTaskScheduler

注意:

ThreadPoolTaskExecutor 和ThreadPoolTaskScheduler都是ThreadPoolExecutor的包装

区别是ThreadPoolTaskScheduler实现了TaskScheduler接口,仅仅多实现这个接口

3、改造service层服务

编写一个测试接口CommonService ,和其实现类CommonServiceImpl

public interface CommonService {
    void executeAsync();
}

image.gif

@Service
public class CommonServiceImpl implements CommonService {
    @Async("taskExecutor")
    @Override
    public void executeAsync() {
        for (int i=0;i<6;i++) {
            System.out.println( "现在i的值--->>:"+i );
            try{
                Thread.sleep(1000);
            }catch(Exception e){
                e.printStackTrace();
            }
        }
    }
}

image.gif

@Async("taskExecutor")     表明executeAsync方法进入的线程池是taskExecutor方法创建的

4、TestController

@RestController
public class TestController {
    @Autowired
    CommonService commonService;
    @GetMapping("/test/executeAsync")
    public String testTASK() {
        commonService.executeAsync();
        return "http请求已结束";
    }
}

image.gif

5、测试

我们用浏览器来测试一下:

image.png

发现当浏览器显示上述提示时,控制台还在打印以下信息,表明该方法以及异步

image.png

6、拓展

 虽然现在线程池能其作用,但是还不清楚线程池的具体使用情况,有多少线程在执行,多少在队列中等待呢?所以创建了一个ThreadPoolTaskExecutor的子类,在每次提交线程的时候都会将当前线程池的运行状况打印出来。这部分参考了这篇博客

springboot线程池的使用和扩展_程序员欣宸的博客-CSDN博客_springboot线程池

public class VisiableThreadPoolTaskScheduler extends ThreadPoolTaskScheduler {
    private static final Logger logger = LoggerFactory.getLogger(VisiableThreadPoolTaskScheduler.class);
    private void showThreadPoolInfo(String prefix){
//        ThreadPoolExecutor threadPoolExecutor = getScheduledExecutor();
        ScheduledThreadPoolExecutor threadPoolExecutor = getScheduledThreadPoolExecutor();
        if(null==threadPoolExecutor){
            return;
        }
        logger.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);
    }
}

image.gif

注意这里是:

getScheduledThreadPoolExecutor()

image.gif

然后修改上面的 TaskPoolConfig  文件

//        ThreadPoolTaskScheduler executor = new ThreadPoolTaskScheduler();
        ThreadPoolTaskScheduler executor = new VisiableThreadPoolTaskScheduler();

image.gif

我们不停的刷新浏览器进行测试,

2019-04-27 13:20:51.693  INFO 18228 --- [nio-8080-exec-7] c.z.o.c.VisiableThreadPoolTaskScheduler  : taskExecutor-, 2. do submit,taskCount [88], completedTaskCount [60], activeCount [28], queueSize [0]
现在i的值--->>:0

image.gif

这说明提交任务到线程池的时候,调用的是submit(Callable task)这个方法,当前已经提交了88个任务,完成了60个,当前有28个线程在处理任务,还剩0个任务在队列中等待,线程池的基本情况一路了然:P



相关文章
|
2月前
|
并行计算 Java 数据处理
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
SpringBoot高级并发实践:自定义线程池与@Async异步调用深度解析
221 0
|
4月前
|
Java 开发者 Spring
【SpringBoot 异步魔法】@Async 注解:揭秘 SpringBoot 中异步方法的终极奥秘!
【8月更文挑战第25天】异步编程对于提升软件应用的性能至关重要,尤其是在高并发环境下。Spring Boot 通过 `@Async` 注解简化了异步方法的实现。本文详细介绍了 `@Async` 的基本用法及配置步骤,并提供了示例代码展示如何在 Spring Boot 项目中创建与管理异步任务,包括自定义线程池、使用 `CompletableFuture` 处理结果及异常情况,帮助开发者更好地理解和运用这一关键特性。
286 1
|
2月前
|
Java 应用服务中间件
面对海量网络请求,Tomcat线程池如何进行扩展?
【10月更文挑战第4天】本文详细探讨了Tomcat线程池相较于标准Java实用工具包(JUC)线程池的关键改进。首先,Tomcat线程池在启动时即预先创建全部核心线程,以应对启动初期的高并发请求。其次,通过重写阻塞队列的入队逻辑,Tomcat能够在任务数超过当前线程数但未达最大线程数时,及时创建非核心线程,而非等到队列满才行动。此外,Tomcat还引入了在拒绝策略触发后重新尝试入队的机制,以提高吞吐量。这些优化使得Tomcat线程池更适应IO密集型任务,有效提升了性能。
面对海量网络请求,Tomcat线程池如何进行扩展?
|
2月前
|
算法 NoSQL Java
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
这篇文章介绍了Spring Boot 3中GraalVM Native Image Support的新特性,提供了将Spring Boot Web项目转换为可执行文件的步骤,并探讨了虚拟线程在Spring Boot中的使用,包括如何配置和启动虚拟线程支持。
129 9
Springboot3新特性:GraalVM Native Image Support和虚拟线程(从入门到精通)
|
1月前
|
Java 开发者 Spring
精通SpringBoot:16个扩展接口精讲
【10月更文挑战第16天】 SpringBoot以其简化的配置和强大的扩展性,成为了Java开发者的首选框架之一。SpringBoot提供了一系列的扩展接口,使得开发者能够灵活地定制和扩展应用的行为。掌握这些扩展接口,能够帮助我们写出更加优雅和高效的代码。本文将详细介绍16个SpringBoot的扩展接口,并探讨它们在实际开发中的应用。
50 1
|
2月前
|
监控 Java 开发者
掌握SpringBoot扩展接口:提升代码优雅度的16个技巧
【10月更文挑战第20天】 SpringBoot以其简化配置和快速开发而受到开发者的青睐。除了基本的CRUD操作外,SpringBoot还提供了丰富的扩展接口,让我们能够更灵活地定制和扩展应用。以下是16个常用的SpringBoot扩展接口,掌握它们将帮助你写出更加优雅的代码。
88 0
|
2月前
|
Java
SpringBoot线程问题
SpringBoot线程问题
28 0
|
3月前
|
Java Spring
运行@Async注解的方法的线程池
自定义@Async注解线程池
179 3
|
4月前
|
Java 开发者 Spring
"揭秘SpringBoot魔法SPI机制:一键解锁服务扩展新姿势,让你的应用灵活飞天!"
【8月更文挑战第11天】SPI(Service Provider Interface)是Java的服务提供发现机制,用于运行时动态查找和加载服务实现。SpringBoot在其基础上进行了封装和优化,通过`spring.factories`文件提供更集中的配置方式,便于框架扩展和组件替换。本文通过定义接口`HelloService`及其实现类`HelloServiceImpl`,并在`spring.factories`中配置,结合`SpringFactoriesLoader`加载服务,展示了SpringBoot SPI机制的工作流程和优势。
67 5
|
4月前
|
Java UED
基于SpringBoot自定义线程池实现多线程执行方法,以及多线程之间的协调和同步
这篇文章介绍了在SpringBoot项目中如何自定义线程池来实现多线程执行方法,并探讨了多线程之间的协调和同步问题,提供了相关的示例代码。
1186 0
下一篇
DataWorks