目录
四.使用线程池来提高Springboot项目的并发处理能力:
(1)通过创建 CompleteableFuture 对象来创建异步任务:
(3)thenRun():完成后执行另一个操作,但不关心结果
(1)thenCombine() – 组合两个独立任务的结果
(3)whenComplete() – 完成后执行处理,无论是否有异常
(2)completeOnTimeout() – 超时后返回默认值
一.什么是线程池:
线程池 是一种用于管理和控制多个工作线程的技术,常用于处理并发任务。线程池的主要作用是避免在执行并发任务时频繁创建和销毁线程,从而提升系统的性能和资源利用率。线程池会维护一定数量的线程,当需要执行任务时,会将任务交给线程池中的空闲线程去执行,而不需要为每个任务创建新的线程。
二.使用线程池的好处:
-
降低资源消耗:通过复用已经创建的线程,减少线程创建和销毁的开销,尤其在高并发情况下,这一点尤为明显。
-
提高响应速度:线程池的线程可以在任务到来时立即执行任务,而不必等待线程创建,减少了响应时间。
-
提高线程管理的灵活性:通过线程池,开发者可以设置线程的数量、任务的队列方式,以及线程的优先级和超时时间等,从而更精细地管理并发任务。
三.线程池的使用场景:
- 并发任务的执行:线程池广泛应用于需要同时处理多个并发任务的场景,例如 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
的方法可以有返回值(异步返回 Future
或 CompletableFuture
),也可以是 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");
}
}
CompletableFuture
是 Java 的异步编程工具之一,它可以用来表示未来某个时间点的结果,并且支持链式调用(如.thenApply()
、.thenAccept()
等)来进一步处理结果。(下面有提及)
CompletableFuture.completedFuture("Task Completed")
是一种快捷方式,表示该CompletableFuture
已经完成并带有指定的值("Task Completed"),这样的返回值可以让调用者拿到异步任务完成后的结果,而不需要等待异步任务手动调用complete()
方法来完成它。
什么时候用
CompletableFuture
?如果你的异步任务需要返回值,并且需要调用者在任务完成后对返回值进行处理,那么返回
CompletableFuture
是一种最佳实践。
- 无返回值: 可以直接用
@Async
标注的方法,如asyncTask()
。- 有返回值: 用
CompletableFuture
封装,便于调用者以非阻塞的方式获取结果。- 组合异步任务:
CompletableFuture
支持任务组合和结果处理,适合用在复杂的异步操作中。
(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); // 处理结果并返回新的值
CompletableFuture.supplyAsync(() -> 100)创建一个异步任务并返回一个结果(这里返回了
100
),然后.thenApply(result -> result * 2) 对异步任务的结果进行处理,并返回一个新的值result -> result * 2也就是200。我们可以调用
future.get()
或future.join()
会得到计算结果200
。
(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()
– 等待多个任务全部完成
只有当所有任务都完成时,
allOf()
返回的CompletableFuture<Void>
才会完成。返回值是
Void
,因为allOf()
本身不会合并各任务的结果,只关注它们是否完成。
CompletableFuture<String> task1 = CompletableFuture.supplyAsync(() -> "Task 1");
CompletableFuture<String> task2 = CompletableFuture.supplyAsync(() -> "Task 2");
CompletableFuture<String> task3 = CompletableFuture.supplyAsync(() -> "Task 3");
CompletableFuture<Void> allFutures = CompletableFuture.allOf(task1, task2, task3);
// 等待所有任务完成
allFutures.join();
// 获取每个任务的结果
try {
String result1 = task1.get();
String result2 = task2.get();
String result3 = task3.get();
System.out.println(result1 + ", " + result2 + ", " + result3);
} catch (Exception e) {
e.printStackTrace();
}
(4)anyOf()
– 任意一个完成
等待多个
CompletableFuture
中任意一个完成并返回第一个完成任务的结果。
CompletableFuture<Object> anyFuture = CompletableFuture.anyOf(
CompletableFuture.supplyAsync(() -> "Task 1"),
CompletableFuture.supplyAsync(() -> "Task 2"),
CompletableFuture.supplyAsync(() -> "Task 3")
);
// 阻塞主线程,直到任意一个任务完成
// 返回值类型是 Object,需要显式转换为具体的类型(如 String)
Object result = anyFuture.join();
System.out.println("First completed task result: " + result);
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()
的第二个参数传递自定义线程池来优化性能。
七.在Springboot项目中使用异步调用:
1.引入依赖:
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter</artifactId>
</dependency>
2.配置异步支持:
启用异步功能,需要在主类或配置类上添加
@EnableAsync
。
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync // 启用异步功能
public class AsyncApplication {
public static void main(String[] args) {
SpringApplication.run(AsyncApplication.class, args);
}
}
3.定义异步服务:
创建一个服务类并使用
@Async
实现异步方法,返回一个CompletableFuture
。
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.concurrent.CompletableFuture;
@Service
public class AsyncService {
@Async
public CompletableFuture<String> fetchData() {
try {
// 模拟耗时操作
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Fetch data in thread: " + Thread.currentThread().getName());
return CompletableFuture.completedFuture("Data fetched successfully");
}
@Async
public CompletableFuture<Integer> calculateSum(int a, int b) {
try {
// 模拟计算操作
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Calculate sum in thread: " + Thread.currentThread().getName());
return CompletableFuture.completedFuture(a + b);
}
}
4.调用异步方法:
在Controller层或service层中调用
CompletableFuture
异步方法。
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.concurrent.CompletableFuture;
@RestController
public class AsyncController {
@Autowired
private AsyncService asyncService;
@GetMapping("/async")
public String executeAsyncTasks() {
CompletableFuture<String> dataFuture = asyncService.fetchData();
CompletableFuture<Integer> sumFuture = asyncService.calculateSum(10, 20);
// 等待所有任务完成
CompletableFuture.allOf(dataFuture, sumFuture).join();
try {
// 获取任务结果
String dataResult = dataFuture.get();
Integer sumResult = sumFuture.get();
return "Results: " + dataResult + ", Sum: " + sumResult;
} catch (Exception e) {
e.printStackTrace();
return "Error occurred";
}
}
}
八.Java后端项目使用场景:
场景描述:
模拟一个订单处理系统:
- 异步获取用户信息。
- 根据用户信息异步获取订单列表。
- 异步计算每个订单的总金额。
- 整合所有结果并返回最终总金额。
1.配置异步支持:
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.scheduling.annotation.EnableAsync;
@SpringBootApplication
@EnableAsync
public class CompletableFutureExampleApplication {
public static void main(String[] args) {
SpringApplication.run(CompletableFutureExampleApplication.class, args);
}
}
2.定义异步任务服务:
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import java.util.List;
import java.util.concurrent.CompletableFuture;
@Service
public class OrderService {
@Async
public CompletableFuture<String> getUserInfo(String userId) {
try {
Thread.sleep(1000); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Fetched user info for userId: " + userId);
return CompletableFuture.completedFuture("User-" + userId);
}
@Async
public CompletableFuture<List<String>> getOrdersByUser(String user) {
try {
Thread.sleep(1500); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("Fetched orders for: " + user);
return CompletableFuture.completedFuture(List.of("Order-1", "Order-2", "Order-3"));
}
@Async
public CompletableFuture<Double> calculateOrderTotal(String orderId) {
try {
Thread.sleep(500); // 模拟耗时操作
} catch (InterruptedException e) {
e.printStackTrace();
}
double total = Math.random() * 100; // 模拟金额计算
System.out.println("Calculated total for " + orderId + ": " + total);
return CompletableFuture.completedFuture(total);
}
}
3.Controller层:
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import java.util.List;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
@RestController
public class OrderController {
@Autowired
private OrderService orderService;
@GetMapping("/process-orders")
public String processOrders(@RequestParam String userId) {
// 异步获取个人信息
CompletableFuture<String> userInfoFuture = orderService.getUserInfo(userId);
// 获取用户信息 -> 获取订单列表 -> 计算每个订单的总金额 -> 汇总
CompletableFuture<Double> totalAmountFuture = userInfoFuture
// 根据用户信息获取订单
.thenCompose(user -> orderService.getOrdersByUser(user))
.thenCompose(orders -> {
// 计算所有订单总金额
// 流式处理订单列表
List<CompletableFuture<Double>> totalFutures = orders.stream()
.map(orderId -> orderService.calculateOrderTotal(orderId))
.exceptionally(ex -> {
System.err.println("Error calculating total for " + orderId + ": " + ex.getMessage());
return 0.0;
})
.toList(); // 转变成集合
// 等待所有金额计算完成
// 将 totalFutures 集合转换为数组,因为 CompletableFuture.allOf() 方法需要数组作为参数。
CompletableFuture<Void> allDone = CompletableFuture.allOf(totalFutures.toArray(new CompletableFuture[0]));
return allDone.thenApply(v -> totalFutures.stream()
// 将每个 CompletableFuture<Double> 转换为 double 值,即每个订单的金额。
.mapToDouble(future -> {
try {
return future.get(); // 获取每个订单的金额
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return 0;
}
})
.sum());
});
// 获取最终结果
try {
double totalAmount = totalAmountFuture.get();
return "Total amount for user " + userId + ": " + totalAmount;
} catch (InterruptedException | ExecutionException e) {
e.printStackTrace();
return "Error processing orders";
}
}
}
到这里,线程池的相关内容想必你已经掌握了许多了,看到这里了,记得给个三连,感谢观看!!!