首页 > 其他分享 >线程池以及详解使用@Async注解异步处理方法

线程池以及详解使用@Async注解异步处理方法

时间:2024-09-10 17:21:43浏览次数:10  
标签:CompletableFuture 异步 任务 线程 Async supplyAsync

目录

一.什么是线程池:

二.使用线程池的好处:

三.线程池的使用场景:

四.使用线程池来提高Springboot项目的并发处理能力:

1.在application.yml配置文件中配置:

2.定义配置类来接受配置文件内的属性值:

3.启用异步支持:

4.实例:

 五.详细解析@Async注解的使用:

1.@Async注解作用:

2.@Async注解的调用过程:

3.@Async注解使用:

(1)启用异步支持:

 (2)定义异步方法:

(3)调用异步方法:

六.CompleteableFuture返回类型的使用:

1.基本使用:

(1)通过创建 CompleteableFuture 对象来创建异步任务:

(2)获取异步方法的结果:

2.链式编程:

(1)thenApply():处理结果并返回新的值

(2)thenAccept():处理结果但不返回值

(3)thenRun():完成后执行另一个操作,但不关心结果

3.组合多个异步任务进行处理:

(1)thenCombine() – 组合两个独立任务的结果

(2)thenCompose() – 链式依赖

(3)allOf() – 等待多个任务全部完成

(4)anyOf() – 任意一个完成

4.异常处理:

(1)exceptionally() – 捕获并处理异常

(2)handle() – 处理正常和异常情况

(3)whenComplete() – 完成后执行处理,无论是否有异常

5.超时处理:

(1)orTimeout() – 设置超时时间

(2)completeOnTimeout() – 超时后返回默认值

6.细节注意:

(1)避免阻塞:

(2)处理异常:

(3)线程池优化:


一.什么是线程池:

线程池 是一种用于管理和控制多个工作线程的技术,常用于处理并发任务。线程池的主要作用是避免在执行并发任务时频繁创建和销毁线程,从而提升系统的性能和资源利用率。线程池会维护一定数量的线程,当需要执行任务时,会将任务交给线程池中的空闲线程去执行,而不需要为每个任务创建新的线程。

二.使用线程池的好处:

  • 降低资源消耗:通过复用已经创建的线程,减少线程创建和销毁的开销,尤其在高并发情况下,这一点尤为明显。

  • 提高响应速度:线程池的线程可以在任务到来时立即执行任务,而不必等待线程创建,减少了响应时间。

  • 提高线程管理的灵活性:通过线程池,开发者可以设置线程的数量、任务的队列方式,以及线程的优先级和超时时间等,从而更精细地管理并发任务。

三.线程池的使用场景:

  • 并发任务的执行:线程池广泛应用于需要同时处理多个并发任务的场景,例如 Web 服务器处理用户请求、数据处理等。
  • 定时任务:通过 ScheduledThreadPoolExecutor 执行定时或周期性任务。
  • 资源受限的场景:在线程数受限的场景中(例如有固定的 CPU 核心数或数据库连接数),线程池能够帮助限制同时执行的线程数,避免资源的过载使用。

四.使用线程池来提高Springboot项目的并发处理能力:

在 Spring Boot 项目中处理高并发时,线程池可以帮助我们管理和优化线程的使用,从而提高系统的并发处理能力。Spring Boot 提供了对线程池的内建支持,我们可以通过配置和编程方式来使用线程池。

1.在application.yml配置文件中配置:

application.yml 中,虽然 Spring Boot 默认没有直接支持线程池配置的属性,但你可以通过自定义配置类来实现线程池的配置。

spring:
  task:
    execution:
      pool:
        core-size: 10  #线程池的核心线程数
        max-size: 50   #线程池的最大线程数
        queue-capacity: 100  #线程池的任务队列容量
        keep-alive: 60s    #线程池中非核心线程的空闲保持时间
        thread-name-prefix: Async-  # 线程池中线程的名称前缀

2.定义配置类来接受配置文件内的属性值:

import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.task.TaskExecutor;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;

import java.util.concurrent.Executor;

@Configuration
public class TaskExecutorConfig {

    @Value("${spring.task.execution.pool.core-size}")
    private int corePoolSize;

    @Value("${spring.task.execution.pool.max-size}")
    private int maxPoolSize;

    @Value("${spring.task.execution.pool.queue-capacity}")
    private int queueCapacity;

    @Value("${spring.task.execution.pool.keep-alive}")
    private String keepAlive;

    @Value("${spring.task.execution.pool.thread-name-prefix}")
    private String threadNamePrefix;

    @Bean(name = "taskExecutor")
    public Executor taskExecutor() {
        ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
        executor.setCorePoolSize(corePoolSize);
        executor.setMaxPoolSize(maxPoolSize);
        executor.setQueueCapacity(queueCapacity);
        executor.setKeepAliveSeconds(Integer.parseInt(keepAlive.replace("s", ""))); // 将 "60s" 转换为 60
        executor.setThreadNamePrefix(threadNamePrefix);
        executor.initialize();
        return executor;
    }
}

3.启用异步支持:

为了确保 Spring Boot 应用程序启用了异步支持,需要在主应用类或者配置类中添加 @EnableAsync 注解:

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

4.实例:

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class MyService {
    
    @Async("taskExecutor")
    public void asyncMethod() {
        // 执行一些耗时操作,打印当前线程的名称
        System.out.println("Executing async method - " + Thread.currentThread().getName());
    }
}

1.@Async 注解用于将一个方法标记为异步执行。标记了 @Async 的方法会在调用时由线程池中的线程异步执行,而不是在调用线程中直接执行。

2.@Async 注解的值 "taskExecutor" 表示使用名为 taskExecutor 的线程池来执行这个异步方法。这个线程池的配置需要在 @Configuration 类中定义(如前面所述)。

 五.详细解析@Async注解的使用:

@Async 注解是 Spring 框架中用于实现异步任务执行的重要功能。它通过将任务放入线程池中执行,从而解耦了任务的执行过程和调用过程,极大地提升了系统的并发处理能力。下面我们来详细解析 @Async 注解的工作原理、配置以及与线程池的结合使用。

1.@Async注解作用:

@Async 用于将标注的方法异步执行,意思是说这个方法的执行不会阻塞调用线程。相反,Spring 会将该方法交给一个单独的线程来处理。这对于需要处理耗时任务的场景(例如,发送邮件、数据库查询、文件操作等)非常有用,能够提高系统响应能力和吞吐量。

2.@Async注解的调用过程:

当调用一个标注了 @Async 的方法时,调用者线程不会等待这个方法执行完成,而是立即返回。实际上 Spring 会将 @Async 方法提交到一个线程池中,线程池中的线程负责实际执行该方法。标注 @Async 的方法可以有返回值(异步返回 FutureCompletableFuture),也可以是 void 类型。如果不指定线程池,Spring 默认使用 SimpleAsyncTaskExecutor。但在实际项目中,通常需要自定义线程池来处理异步任务。

3.@Async注解使用:

(1)启用异步支持:

首先需要在配置类或 Spring Boot 主类中启用异步支持:

不加@EnableAsync注解,那么@Async注解不会生效。

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;

@SpringBootApplication
@EnableAsync // 启用异步任务支持
public class Application {
    public static void main(String[] args) {
        SpringApplication.run(Application.class, args);
    }
}

 (2)定义异步方法:

在 Spring 的服务类中,可以通过 @Async 注解来定义异步方法。方法的返回类型可以是 void,Future<T>,CompleteableFuture<T>,所以我们需要用这三种来声明返回值类型。

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;

@Service
public class MyAsyncService {

    @Async
    public void asyncTask() {
        // 异步执行的任务
        System.out.println("Executing task in: " + Thread.currentThread().getName());
    }

    @Async
    public CompletableFuture<String> asyncTaskWithReturn() {
        // 异步执行的任务并返回结果
        System.out.println("Executing task with return in: " + Thread.currentThread().getName());
        return CompletableFuture.completedFuture("Task Completed");
    }
}

(3)调用异步方法:

异步方法会在后台线程池中执行,调用线程不会等待其执行结果:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
public class MyController {

    @Autowired
    private MyAsyncService myAsyncService;

    @GetMapping("/async")
    public String testAsync() {
        // 调用异步方法
        myAsyncService.asyncTask();
        return "Task started!";
    }

    @GetMapping("/asyncReturn")
    public CompletableFuture<String> testAsyncWithReturn() {
        // 调用异步方法并获取返回结果
        return myAsyncService.asyncTaskWithReturn();
    }
}

六.CompleteableFuture返回类型的使用:

1.CompleteableFuture 是 Java 8 引入的一种用于异步编程的工具类,它是 Future 的扩展,提供了丰富的异步编程方法。CompleteableFuture 的强大之处在于其灵活性和丰富的 API,可以轻松处理异步任务的组合、链式调用、异常处理等。

2.异步方法可以返回 CompleteableFuture,以便调用方可以在合适的时机检查任务是否完成,并获取任务的结果。

@Async
public CompletableFuture<String> asyncMethodWithCompletableFuture() throws InterruptedException {
    Thread.sleep(1000); // 模拟耗时任务
    return CompletableFuture.completedFuture("Result from async task");
}

1.基本使用:

(1)通过创建 CompleteableFuture 对象来创建异步任务:

// 手动创建一个未完成的CompletableFuture对象
CompletableFuture<String> future = new CompletableFuture<>();

// 使用supplyAsync创建异步任务
CompletableFuture<String> future1 = CompletableFuture.supplyAsync(() -> {
    return "Hello, World!";
});

// 使用runAsync创建没有返回值的异步任务
CompletableFuture<Void> future2 = CompletableFuture.runAsync(() -> {
    System.out.println("Running a task asynchronously.");
});

(2)获取异步方法的结果:

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Result");

// get() 会阻塞当前线程直到结果可用
String result = future.get();

// getNow() 如果结果可用则立即返回,否则返回默认值
String resultNow = future.getNow("Default Value");

// join() 类似于 get(),但不会抛出受检异常
String resultJoin = future.join();

2.链式编程:

CompletableFuture 允许你通过链式调用来构建一系列的异步任务,常用的方法有 thenApply()thenAccept()thenRun()

(1)thenApply():处理结果并返回新的值

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> 100)
    .thenApply(result -> result * 2); // 处理结果并返回新的值

(2)thenAccept():处理结果但不返回值

CompletableFuture.supplyAsync(() -> "Hello")
    .thenAccept(result -> System.out.println(result)); // 打印结果

(3)thenRun():完成后执行另一个操作,但不关心结果

CompletableFuture.supplyAsync(() -> "Task")
    .thenRun(() -> System.out.println("Task completed!"));

3.组合多个异步任务进行处理:

有时我们需要同时执行多个异步任务,并在它们完成时进行进一步处理。CompletableFuture 提供了多个方法来组合任务,例如 thenCombine()thenCompose()allOf()anyOf()

(1)thenCombine() – 组合两个独立任务的结果

CompletableFuture<Integer> future1 = CompletableFuture.supplyAsync(() -> 100);
CompletableFuture<Integer> future2 = CompletableFuture.supplyAsync(() -> 200);

CompletableFuture<Integer> combinedFuture = future1.thenCombine(future2, (result1, result2) -> result1 + result2);

Integer combinedResult = combinedFuture.join(); // 300

(2)thenCompose() – 链式依赖

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> "Hello")
    .thenCompose(result -> CompletableFuture.supplyAsync(() -> result + " World"));

String combinedResult = future.join(); // "Hello World"

(3)allOf() – 等待多个任务全部完成

CompletableFuture<Void> allFutures = CompletableFuture.allOf(
    CompletableFuture.supplyAsync(() -> "Task 1"),
    CompletableFuture.supplyAsync(() -> "Task 2"),
    CompletableFuture.supplyAsync(() -> "Task 3")
);

allFutures.join(); // 等待所有任务完成

(4)anyOf() – 任意一个完成

CompletableFuture<Void> allFutures = CompletableFuture.allOf(
    CompletableFuture.supplyAsync(() -> "Task 1"),
    CompletableFuture.supplyAsync(() -> "Task 2"),
    CompletableFuture.supplyAsync(() -> "Task 3")
);

allFutures.join(); // 等待所有任务完成

4.异常处理:

CompletableFuture 提供了几种方式来处理异常,包括 exceptionally()handle()whenComplete()。 

(1)exceptionally() – 捕获并处理异常

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    if (Math.random() > 0.5) {
        throw new RuntimeException("Something went wrong!");
    }
    return 100;
}).exceptionally(ex -> {
    System.out.println("Caught exception: " + ex.getMessage());
    return -1; // 返回默认值
});

Integer result = future.join();

(2)handle() – 处理正常和异常情况

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    if (Math.random() > 0.5) {
        throw new RuntimeException("Error occurred!");
    }
    return 100;
}).handle((result, ex) -> {
    if (ex != null) {
        System.out.println("Caught exception: " + ex.getMessage());
        return -1; // 返回默认值
    }
    return result * 2; // 处理结果
});

Integer finalResult = future.join();

(3)whenComplete() – 完成后执行处理,无论是否有异常

CompletableFuture<Integer> future = CompletableFuture.supplyAsync(() -> {
    if (Math.random() > 0.5) {
        throw new RuntimeException("Error occurred!");
    }
    return 100;
}).whenComplete((result, ex) -> {
    if (ex != null) {
        System.out.println("Task completed with exception: " + ex.getMessage());
    } else {
        System.out.println("Task completed successfully with result: " + result);
    }
});

5.超时处理:

CompletableFuture 还支持在异步任务中设置超时,以避免长时间等待。

(1)orTimeout() – 设置超时时间

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Completed";
}).orTimeout(1, TimeUnit.SECONDS); // 超时时间为1秒

future.exceptionally(ex -> {
    System.out.println("Task timed out");
    return "Timeout";
});

(2)completeOnTimeout() – 超时后返回默认值

CompletableFuture<String> future = CompletableFuture.supplyAsync(() -> {
    try {
        Thread.sleep(2000);
    } catch (InterruptedException e) {
        e.printStackTrace();
    }
    return "Completed";
}).completeOnTimeout("Default Value", 1, TimeUnit.SECONDS); // 超时返回默认值

6.细节注意:

(1)避免阻塞:

尽量避免使用 get()join() 直接阻塞主线程,尽可能使用链式操作或回调来处理异步结果。

(2)处理异常:

确保在异步操作中有良好的异常处理策略,尤其是在组合多个任务时,使用 exceptionally()handle() 捕获和处理异常。

(3)线程池优化:

默认的 CompletableFuture 使用 ForkJoinPool 来执行异步任务。对于高并发的场景,你可以通过 supplyAsync()runAsync() 的第二个参数传递自定义线程池来优化性能。


到这里,线程池的相关内容想必你已经掌握了许多了,看到这里了,记得给个三连,感谢观看!!!

标签:CompletableFuture,异步,任务,线程,Async,supplyAsync
From: https://blog.csdn.net/2302_79840586/article/details/142102263

相关文章

  • 面试官:如何实现线程池任务编排?
    任务编排(TaskOrchestration)是指管理和控制多个任务的执行流程,确保它们按照预定的顺序正确执行。1.为什么需要任务编排?在复杂的业务场景中,任务间通常存在依赖关系,也就是某个任务会依赖另一个任务的执行结果,在这种情况下,我们需要通过任务编排,来确保任务按照正确的顺序进行执......
  • 最简单C++线程和互斥锁使用示例
    std::thread是C++11标准库中引入的一个类,用于表示一个独立的执行线程。而std::mutex是C++11中提供的一种互斥锁,用于在多个线程间同步对共享数据的访问,以避免数据竞争和条件竞争。下面将分别介绍std::thread和std::mutex的基本使用,并通过一个示例展示它们的结合使用......
  • JAVA多线程-如何保证线程安全
    线程安全:指在多线程对一个共享资源同时进行操作时,所得到的结果都是一样的如何保证线程安全方法:要保证线程安全,就必须保证线程同步,保证线程的可见性,有序性,和原子性线程同步线程同步的含义和字面意思相反,同步其实是线程"排队"的意思,就是让线程按照一定的顺序执......
  • java多线程转换文件格式
    privatestaticfinalintTHREAD_COUNT=4;//线程数privatestaticfinalintBUFFER_SIZE=1024;//缓冲区大小/***多线程读取文件,转换文件编码格式4线程1Mb缓存**@paraminputFile输入文件Stringinput="E:/02code/web/test.txt"......
  • Java学习 - 多线程第二部分
    1.线程池1.1线程状态介绍当线程被创建并启动以后,它既不是一启动就进入了执行状态,也不是一直处于执行状态。线程对象在不同的时期有不同的状态。那么Java中的线程存在哪几种状态呢?Java中的线程状态被定义在了java.lang.Thread.State枚举类中,State枚举类的源码如下:publi......
  • js中【异步编程】超详细解读,看这一篇就够了
    一、JavaScript异步编程概述JavaScript是一门单线程语言,这意味着它同一时间只能执行一个任务。在现代Web开发中,异步编程变得尤为重要,因为许多任务(如网络请求、文件读取、定时器等)需要大量时间,如果使用同步编程模型,这些任务会阻塞整个线程,导致页面或程序卡顿。为了解决这个......
  • 进程中的线程调度
     进程是应用程序运行的基本单位。进程是计算机资源的调度过程。资源抢占着计算机的运行内存。一个应用服务的启动开启一个进程。完整的进程包括主线程,用户线程和守护线程。当一个应用程序服务开启的时候,主线程处于运行状态。用户线程分为父级用户线程和子线程。 计算机的组成......
  • 小琳Python课堂:掌握进程与线程的奥秘
    大家好,这里是小琳Python课堂!今天我们来探讨Python中的多任务处理利器——进程和线程!......
  • 并发编程:线程池(上)
    一、什么是线程池?顾名思义,线程池就是管理一系列线程的资源池。当有任务要处理时,直接从线程池中获取线程来处理,处理完之后线程并不会立即被销毁,而是等待下一个任务。二、为什么要用线程池?池化技术想必大家已经屡见不鲜了,线程池、数据库连接池、HTTP连接池等等都是对这个思......
  • 并发编程:线程池(下)
    一、线程池常用的阻塞队列有哪些?新任务来的时候会先判断当前运行的线程数量是否达到核心线程数,如果达到的话,新任务就会被存放在队列中。不同的线程池会选用不同的阻塞队列,我们可以结合内置线程池来分析。容量为Integer.MAX_VALUE的LinkedBlockingQueue(有界阻塞队列):FixedT......