springboot中多线程的使用

多线程基本概念

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

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

线程池

背景:经常创建和销毁,使用量特别大的资源,比如并发情况下的线程,对性能影响很大。
思路:提前创建好多个线程,放入线程池之,使用时直接获取,使用完放回池中。可以避免频繁创建销毁,实现重复利用。类似生活中的公共交通工具。
好处:提高响应速度(减少了创建新线程的时间)
降低资源消耗(重复利用线程池中线程,不需要每次都创建)
便于线程管理
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 futureInt = CompletableFuture.supplyAsync(() -> 10);
    CompletableFuture futureStr = futureInt.thenApply(result -> "Result is: " + result);

参考链接

SpringBoot使用多线程

CompletableFuture 和 Future 的区别

CompletableFuture 常用方法

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值