0


springboot中多线程的使用

多线程基本概念

程序,进程,线程的基本概念

程序:是为完成特定任务,用某种语言编写的一组指令的集合,即指一段静态的代码,静态对象。
进程:是程序的一次执行过程,或是正在运行的一个程序,是一个动态的过程,有它自身的产生,存在和消亡的过程。-------生命周期
线程:进程可进一步细化为线程,是一个程序内部的一条执行路径

线程池

背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
思路:提前创建好多个线程,放入线程池之,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。类似生活中的公共交通工具。
好处:提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程管理
corePoolSize:核心池的大小
maximumPoolSize:最大线程数
keepAliveTime:线程没有任务时最多保持多长时间后会终止
。。。。。。

多线程的使用

线程的创建方式

  • 创建一个继承于Thread类的子类 (通过ctrl+o(override)输入run查找run方法)
  • 重写Thread类的run()方法
  • 创建Thread子类的对象
  • 通过此对象调用start()方法

主要重点是多线程在springboot中的使用

线程池的创建、配置

@Configuration@EnableAsyncpublicclassAsyncConfiguration{@Bean("doSomethingExecutor")publicExecutordoSomethingExecutor(){ThreadPoolTaskExecutor executor =newThreadPoolTaskExecutor();// 核心线程数:线程池创建时候初始化的线程数
        executor.setCorePoolSize(10);// 最大线程数:线程池最大的线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程
        executor.setMaxPoolSize(20);// 缓冲队列:用来缓冲执行任务的队列
        executor.setQueueCapacity(500);// 允许线程的空闲时间60秒:当超过了核心线程之外的线程在空闲时间到达之后会被销毁
        executor.setKeepAliveSeconds(60);// 线程池名的前缀:设置好了之后可以方便我们定位处理任务所在的线程池
        executor.setThreadNamePrefix("do-something-");// 缓冲队列满了之后的拒绝策略:由调用线程处理(一般是主线程)
        executor.setRejectedExecutionHandler(newThreadPoolExecutor.DiscardPolicy());
        executor.initialize();return executor;}}

异步任务的创建

使用的方式非常简单,在需要异步的方法上加@Async注解

@Slf4j@ServicepublicclassAsyncService{// 指定使用beanname为doSomethingExecutor的线程池@Async("doSomethingExecutor")publicStringdoSomething(String message){
        log.info("do something, message={}", message);try{Thread.sleep(1000);}catch(InterruptedException e){
            log.error("do something error: ", e);}return message;}}

注意事项

  • @Async("doSomethingExecutor") 异步任务需要指定对应的线程池未配置线程池,使用的是默认的ThreadPoolTaskExecutor,corePoolSize=8,maxPoolSize、queueCapacity=Integer.MAX_VALUE
  • @Async注解会在以下几个场景失效,也就是说明明使用了@Async注解,但就没有走多线程 (1)异步方法使用static关键词修饰 (2)异步类不是一个Spring容器的bean(一般使用注解@Component和@Service,并且能被Spring扫描到) (3)SpringBoot应用中没有添加@EnableAsync注解 (4)在同一个类中,一个方法调用另外一个有@Async注解的方法,注解不会生效。原因是@Async注解的方法,是在代理类中执行的

注意:异步方法使用注解@Async的返回值只能为void或者Future及其子类,当返回结果为其他类型时,方法还是会异步执行,但是返回值都是null

Spring Boot的异步任务是对原生Java多线程的封装,使得开发人员无需关心底层线程池的具体配置和管理,可以更加专注于业务逻辑。

任务执行结果的获取、异常的捕捉

任务执行结果的获取

通过Future获取异步任务的执行结果

异步任务返回

// 异步执行的方法, 注解内为自定义线程池类名@Override@Async("asyncExecutor")publicFuture<Integer>test(Integer i){
    log.info("{}: {}", i,Thread.currentThread().getName());ThreadUtil.sleep(1,TimeUnit.SECONDS);
    log.info("@Async执行:{}", i);returnnewAsyncResult(i);}

获取任务的返回值

Future<Integer> future1 = testService.test(1);Future<Integer> future2 = testService.test(2);// 阻塞,直至 future1 的异步线程执行完毕
log.info("future1结果:{}", future1.get());// 阻塞,直至 future2 的异步线程执行完毕
log.info("future1结果:{}", future2.get());

也可以使用Java 8 中引入的的java.util.concurrent.CompletableFuture实现任务结果的获取;

捕捉任务执行的异常

  • 方法一:子线程中try… catch… 最简单有效的方法就是:直接在子线程中进行异常捕获处理,如下代码:
publicclassChildThreadimplementsRunnable{publicvoidrun(){doSomething1();try{// 可能发生异常的方法exceptionMethod();}catch(Exception e){// 处理异常System.out.println(String.format("handle exception in child thread. %s", e));}doSomething2();}}
  • 方法二:通过Future的get方法捕捉异常使用线程池提交一个能获取到返回信息的方法,也就是ExecutorService.submit(Callable)在submit之后可以获得一个线程执行结果的Future对象,而如果子线程中发生了异常,通过future.get()获取返回值时,可以捕获到ExecutionException异常,从而知道子线程中发生了异常。代码如下:
publicclassMain{publicstaticvoidmain(String[] args){ExecutorService executorService =Executors.newFixedThreadPool(8);Future future = executorService.submit(newChildThread());try{
            future.get();}catch(InterruptedException e){System.out.println(String.format("handle exception in child thread. %s", e));}catch(ExecutionException e){System.out.println(String.format("handle exception in child thread. %s", e));}finally{if(executorService !=null){
                executorService.shutdown();}}}}
CompletableFuture

是 Java 8 引入的类,扩展了 Future 的功能,提供了更强大和灵活的异步编程支持。它允许更容易地处理异步操作的结果、异常和组合,以及在操作完成时执行回调等。

  1. 组合和链式操作

Future:Future 没有提供内置的操作符来对多个异步操作进行链式组合。

CompletableFuture:CompletableFuture 支持在操作完成后进行链式操作,使多个异步操作可以依次执行,以及在其中一个或多个操作完成后执行其他操作。

  • allOf(CompletableFuture<?>... cfs)

返回一个新的CompletableFuture,当所有给定的CompletableFuture都完成后,这个新的Future才完成。

  • anyOf(CompletableFuture<?>... cfs)

当任何一个给定的CompletableFuture完成后,这个新的Future就完成。

CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> loadFromDB1());
CompletableFuture<String> future2 = CompletableFuture.supplyAsync(() -> loadFromDB2());

// 等待所有任务完成
CompletableFuture<Void> allDone = CompletableFuture.allOf(future1, future2);

// 获取所有结果
CompletableFuture<List<String>> combined = allDone.thenApply(v -> Arrays.asList(future1.join(), future2.join()));
  1. 异常处理

Future:Future 的异常处理相对有限,通常需要使用 try-catch 块来捕获操作过程中的异常。

CompletableFuture:CompletableFuture 具有更强大的异常处理机制,可以使用 exceptionally()、handle() 等方法来处理操作过程中的异常。

  • exceptionally(Function<Throwable,? extends T> fn)

当CompletableFuture遇到异常时,使用给定的函数处理异常并返回一个替代结果。

CompletableFuture<String> future =...;CompletableFuture<String> fallback = future.exceptionally(ex ->"Fallback Value");
  • handle(BiFunction<? super T, Throwable, ? extends U> fn)

不管是正常完成还是异常完成,都会调用给定的函数来处理结果或异常。

CompletableFuture<String> future =...;CompletableFuture<Object> handled = future.handle((result, ex)->{if(ex !=null){returnhandleException(ex);}else{returnprocessSuccess(result);}});
  1. 回调执行

Future:Future 不支持在操作完成时执行回调操作。

CompletableFuture:CompletableFuture 支持使用 thenApply()、thenCompose()、thenCombine() 等方法来在操作完成后执行回调。

  • thenApply(Function<? super T,? extends U> fn) 在前一个任务完成之后,根据前一个任务的结果执行一个函数并返回新的CompletableFuture

  • thenCompose(Function<? super T,? extends CompletionStage<U>> fn) 类似于thenApply,但是返回的是一个新的CompletableFuture,适用于任务之间有依赖的情况。

CompletableFuture<Integer> futureInt =CompletableFuture.supplyAsync(()->10);CompletableFuture<String> futureStr = futureInt.thenApply(result ->"Result is: "+ result);

参考链接

SpringBoot使用多线程

CompletableFuture 和 Future 的区别

CompletableFuture 常用方法

标签: spring boot java spring

本文转载自: https://blog.csdn.net/qq_36996785/article/details/140337366
版权归原作者 zhouzipeng_cqu 所有, 如有侵权,请联系我们删除。

“springboot中多线程的使用”的评论:

还没有评论