锋盈数科-知识库 Logo
首页
软件开发
计算机基础
Hello Halo
新手必读
关于本知识库
登录 →
锋盈数科-知识库 Logo
首页 软件开发 计算机基础 Hello Halo 新手必读 关于本知识库
登录
  1. 首页
  2. 软件开发
  3. JAVA
  4. springboot中多线程的使用

springboot中多线程的使用

0
  • JAVA
  • 发布于 2024-08-10
  • 0 次阅读
黄健
黄健

本文由 简悦 SimpRead 转码, 原文地址 blog.csdn.net

多线程基本概念

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

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

线程池

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

多线程的使用

线程的创建方式

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

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

线程池的创建、配置

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

异步任务的创建

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

@Slf4j
@Service
public class AsyncService {
    // 指定使用beanname为doSomethingExecutor的线程池
    @Async("doSomethingExecutor")
    public String doSomething(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")
public Future<Integer> test(Integer i) {
    log.info("{}: {}", i, Thread.currentThread().getName());
    ThreadUtil.sleep(1, TimeUnit.SECONDS);
    log.info("@Async执行:{}", i);
    return new AsyncResult(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… 最简单有效的方法就是:直接在子线程中进行异常捕获处理,如下代码:
public class ChildThread implements Runnable {
    public void run() {
        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 异常,从而知道子线程中发生了异常。代码如下:

public class Main {
    public static void main(String[] args) {
        ExecutorService executorService = Executors.newFixedThreadPool(8);
        Future future = executorService.submit(new ChildThread());
        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) {
        return handleException(ex);
    } else {
        return processSuccess(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 常用方法

标签: #JAVA 991
相关文章

Spring 实现 3 种异步接口 2024-10-18 09:07

大家好,我是苏三~ 如何处理比较耗时的接口? 这题我熟,直接上异步接口,使用 Callable、WebAsyncTask 和 DeferredResult、CompletableFuture等均可实现。 但这些方法有局限性,处理结果仅返回单个值。在某些场景下,如果需要接口异步处理的同时,还持续不断地

重学SpringBoot3-集成Redis(五)之布隆过滤器 2024-10-08 11:24

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-集成Redis(五)之布隆过滤器 1. 什么是布隆过滤器? * 基本概念 适用场景 2. 使用 Redis 实现布隆过滤器 * 项目依赖 Redis 配置

SpringBoot整合异步任务执行 2024-10-08 11:24

同步任务: 同步任务是在单线程中按顺序执行,每次只有一个任务在执行,不会引发线程安全和数据一致性等 并发问题 同步任务需要等待任务执行完成后才能执行下一个任务,无法同时处理多个任务,响应慢,影响用 户体验 异步任务: 异步任务是在多线程中同时执行,多个任务可以并发执行,同时处理多个请求,响应快,资源

springboot kafka多数据源,通过配置动态加载发送者和消费者 2024-10-08 11:24

前言 最近做项目,需要支持kafka多数据源,实际上我们也可以通过代码固定写死多套kafka集群逻辑,但是如果需要不修改代码扩展呢,因为kafka本身不处理额外逻辑,只是起到削峰,和数据的传递,那么就需要对架构做一定的设计了。 准备test kafka本身非常容易上手,如果我们需要单元测试,引入ja

SpringBoot 集成 Redis 2024-10-08 11:24

一:SpringBoot 集成 Redis ①Redis是一个 NoSQL(not only)数据库, 常作用缓存 Cache 使用。 ②Redis是一个中间件、是一个独立的服务器;常用的数据类型: string , hash ,set ,zset , list ③通过Redis客户端可以使用多种语

SpringBoot整合QQ邮箱 2024-10-08 11:24

SpringBoot可以通过导入依赖的方式集成多种技术,这当然少不了我们常用的邮箱,现在本章演示SpringBoot整合QQ邮箱发送邮件…. 下面按步骤进行: 1.获取QQ邮箱授权码 1.1 登录QQ邮箱 1.2 开启SMTP服务 找到下图中的SMTP服务区域,如果当前账号未开启的话自己手动开启。

目录

IT 外包服务商

  • 意见投递
  • zyf6619

软件开发应用

主菜单

  • 首页
  • 软件开发
  • 计算机基础
  • Hello Halo
  • 新手必读
  • 关于本知识库
Copyright © 2024 your company All Rights Reserved. Powered by Halo.