SpringBoot中使用多线程的方法示例

作者:BouncingFish 时间:2021-05-24 19:56:04 

一、介绍

Spring是通过任务执行器(TaskExecutor)来实现多线程和并发编程,使用Spring提供的ThreadPoolTaskExecutor来创建一个基于线城池的TaskExecutor。在使用线程池的大多数情况下都是异步非阻塞的。节省更多的时间,提高效率。

工作原理

SpringBoot中使用多线程的方法示例

当主线程中调用execute接口提交执行任务时:则执行以下步骤:注意:线程池初始时,是空的。

  • 如果当前线程数<corePoolSize,如果是则创建新的线程执行该任务

  • 如果当前线程数>=corePoolSize,则将任务存入BlockingQueue

  • 如果阻塞队列已满,且当前线程数<maximumPoolSize,则新建线程执行该任务。

  • 如果阻塞队列已满,且当前线程数>=maximumPoolSize,则抛出异常RejectedExecutionException,告诉调用者无法再接受任务了。

在Springboot中对其进行了简化处理,只需要配置一个类型为java.util.concurrent.TaskExecutor或其子类的bean,并在配置类或直接在程序入口类上声明注解@EnableAsync,即可可以开启异步任务。

调用也简单,在由Spring管理的对象的方法上标注注解@Async,声明是异步任务,显式调用即可生效。

二、声明

让配置类实现AsyncConfigurer接口,并重写getAsyncExecutor方法,并返回一个ThreasPoolTaskExecutor,就可以获取一个基于线程池的TaskExecutor
使用注解@EnableAsync开启异步,会自动扫描


@Configuration
@EnableAsync
public class ThreadConfig implements AsyncConfigurer {

@Override
 public Executor getAsyncExecutor() {
   ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
   executor.setCorePoolSize(5);
   executor.setMaxPoolSize(15);
   executor.setQueueCapacity(25);
   executor.initialize();
   return executor;
 }

@Override
 public AsyncUncaughtExceptionHandler getAsyncUncaughtExceptionHandler() {
   return null;
 }
}

三、调用

通过@Async注解表明该方法是异步方法,如果注解在类上,那表明这个类里面的所有方法都是异步的


@Service
public class AsyncTaskService {

@Async
 public void executeAsyncTask(int i) {
   System.out.println("线程" + Thread.currentThread().getName() + " 执行异步任务:" + i);
 }

}

四、进阶

有时候我们不止希望异步执行任务,还希望任务执行完成后会有一个返回值,在java中提供了Future泛型接口,用来接收任务执行结果,springboot也提供了此类支持,使用实现了ListenableFuture接口的类如AsyncResult来作为返回值的载体。比如上例中,我们希望返回一个类型为String类型的值,可以将返回值改造为:


 @Async
 public Future<String> executeAsyncTaskWithResult2(int i) {
   System.out.println("线程" + Thread.currentThread().getName() + " 开始执行异步任务" + i);
   try {
     Thread.sleep(10000);
   } catch (InterruptedException e) {
     e.printStackTrace();
   }
   System.out.println("线程" + Thread.currentThread().getName() + " 结束执行异步任务" + i);
   return new AsyncResult<>("线程" + Thread.currentThread().getName() + " 执行异步任务:" + i);
 }

调用返回值:
get()是阻塞式,等待当前线程完成才返回值


 public void threadTest() {
   try {
     List<Future> futures = new ArrayList<>();
     for (int i = 0; i < 20; i++) {
       futures.add(asyncTaskService.executeAsyncTaskWithResult2(i));
     }
     // 获取值。get是阻塞式,等待当前线程完成才返回值
     for (Future<String> future : futures) {
       System.out.println("返回结果:" + future.get());
     }
   } catch (InterruptedException e) {
     e.printStackTrace();
   } catch (ExecutionException e) {
     e.printStackTrace();
   }
 }

补充

实际上,@Async还有一个参数,通过Bean名称来指定调用的线程池-比如上例中设置的线程池参数不满足业务需求,可以另外定义合适的线程池,调用时指明使用这个线程池-缺省时springboot会优先使用名称为'taskExecutor'的线程池,如果没有找到,才会使用其他类型为TaskExecutor或其子类的线程池。

来源:https://juejin.cn/post/6940133431485399047

标签:SpringBoot,多线程
0
投稿

猜你喜欢

  • spring boot的拦截器简单使用示例代码

    2021-09-29 04:07:07
  • Java枚举类型enum的详解及使用

    2023-08-02 14:23:57
  • 全面分析Java文件上传

    2021-12-09 13:22:52
  • 详解Java代码常见优化方案

    2023-11-29 03:13:04
  • SpringBoot整合Elasticsearch游标查询的示例代码(scroll)

    2022-02-11 02:02:13
  • Spring创建bean实例的几种方式分享

    2022-02-21 22:08:19
  • java实现短信验证码5分钟有效时间

    2023-08-30 18:42:26
  • 分析并发编程之LongAdder原理

    2023-05-11 17:19:30
  • springboot docker jenkins 自动化部署并上传镜像的步骤详解

    2023-07-28 01:54:38
  • Java ThreadPoolExecutor 线程池的使用介绍

    2021-06-28 12:40:35
  • Java利用移位运算将int型分解成四个byte型的方法

    2023-11-09 08:25:00
  • Java中的静态绑定和动态绑定详细介绍

    2023-01-18 19:54:06
  • Mybatis 动态SQL的几种实现方法

    2023-11-10 12:15:15
  • sqlite数据库的介绍与java操作sqlite的实例讲解

    2023-05-09 03:07:40
  • Java序列化JSON丢失精度问题的解决方法(修复Long类型太长)

    2022-10-15 00:01:34
  • 基于C#实现FTP下载文件

    2021-07-09 20:10:01
  • Java利用POI读取、写入Excel的方法指南

    2023-11-23 15:39:23
  • C#中实现Json序列化与反序列化的几种方式

    2021-11-06 01:31:07
  • Java中的ThreadLocal详解

    2023-01-02 16:02:21
  • Java webservice的POST和GET请求调用方式

    2023-01-10 05:35:43
  • asp之家 软件编程 m.aspxhome.com