springboot使用线程池的实际应用(一)

news/2025/1/13 11:50:01/

好的,这里是一个多线程应用于大量数据处理并且返回数据库的例子。

假设我们需要从数据库中查询所有用户的信息,然后对这些用户数据进行批量处理,并将结果写回数据库中。在处理大量数据时,单个线程可能会花费很长时间来完成任务,而使用多线程可以提高处理效率。下面是一个基于Java多线程处理大量数据的示例代码:

ExecutorService executor = Executors.newFixedThreadPool(10);
List<User> userList = userDao.findAll();
List<CompletableFuture<Void>> futures = new ArrayList<>();for (User user : userList) {CompletableFuture<Void> future = CompletableFuture.runAsync(() -> {// 根据每个用户的信息进行批量处理逻辑// ...userDao.update(user); // 更新用户数据}, executor);futures.add(future);
}// 等待所有异步操作完成并合并结果
CompletableFuture.allOf(futures.toArray(new CompletableFuture[0])).join();

这段代码首先创建了一个大小为10的线程池,然后从数据库中查询出所有用户数据。接着,它遍历用户列表,将每个用户的处理逻辑封装成一个异步任务提交给线程池处理,并将每个异步任务的CompletableFuture对象放入futures集合中。

在异步任务全部提交完成后,我们调用CompletableFuture.allOf()方法合并所有CompletableFuture对象,并调用join()方法等待所有异步操作完成。此时所有用户的处理操作都会在各自线程中并发执行,当所有用户处理完成后,我们可以将结果回写到数据库中。

需要注意的是,在多线程并发处理数据时,我们需要防止多个线程同时读写同一个数据。在上面的代码示例中,我们使用了线程安全的ExecutorService线程池,并且在更新用户数据时,使用了线程安全的数据库操作对象。这样可以确保在多线程环境下操作数据是安全的。

下面是一个示例代码,使用Spring Boot注解,采用多线程进行用户查询数据库、处理和写入数据库的操作:

import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;@Component
public class UserProcessor {private final UserDao userDao;public UserProcessor(UserDao userDao) {this.userDao = userDao;}@Asyncpublic void handleUser(Long userId) {User user = userDao.findById(userId);// 对用户数据进行处理// ...userDao.update(user); // 写入用户数据到数据库}}

在该示例中,我们声明了一个名为UserProcessor的SpringBean,并使用@Async注解标注handleUser()方法,表示该方法可以异步执行。handleUser()方法接收用户的ID作为参数,先通过该ID查询出对应的用户数据,随后对该用户数据进行处理,最后将处理结果写入数据库中。

在使用Spring Boot时,只需在配置类中添加@EnableAsync注解启用异步编程功能,例如:

import org.springframework.context.annotation.Configuration;
import org.springframework.scheduling.annotation.EnableAsync;@Configuration
@EnableAsync
public class AsyncConfig {}

在上述配置类中,我们使用@Configuration注解将其定义为一个配置类,并使用@EnableAsync注解启用异步编程功能。这样,在Spring容器启动时,所有被@Async注解标记的异步方法将被Spring框架识别并使用线程池异步运行。

最后,我们可以在Service层或Controller层调用UserProcessor的handleUser()方法,例如:

import org.springframework.stereotype.Service;@Service
public class UserService {private final UserProcessor userProcessor;public UserService(UserProcessor userProcessor) {this.userProcessor = userProcessor;}public void handleUser(Long userId) {userProcessor.handleUser(userId);}}

这样,当调用UserService的handleUser()方法时,Spring框架会异步执行UserProcessor的handleUser()方法,实现并发处理和写入数据到数据库的功能。

那么你在controller层调用下吧

好的,以下是在Controller层调用示例:

import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RestController;@RestController
public class UserController {private final UserService userService;public UserController(UserService userService) {this.userService = userService;}@GetMapping("/users/{userId}")public void handleUser(@PathVariable Long userId) {userService.handleUser(userId);}
}

在上述示例中,我们使用@GetMapping注解定义了一个资源路径为“/users/{userId}”的接口,接口以路径变量“userId”作为参数,调用了UserService的handleUser()方法。当这个接口被请求时,Spring框架会将请求的路径变量“userId”注入到handleUser()方法的参数中,最终异步地处理并写入对应用户的数据到数据库中。

假如我要执行一个很长的service方法,各种修改数据库,里面就调用了一个这样的方法 @Async
public void handleUser(Long userId) {
User user = userDao.findById(userId);
// 对用户数据进行处理
// ...
userDao.update(user); // 写入用户数据到数据库
}然后外面还能加事务注解吗?还能执行成功吗?

当您在一个被@Transactional标记的方法中调用带有@Async注解的异步方法时,事务环境可能受到影响,导致事务提交不一致或一些事务操作失效。

原因如下:

  • 异步方法与调用者方法处于不同的线程中,存在线程间共享的数据不一致的风险。
  • Spring通过创建子线程实现异步方法的调用,并且异步线程与调用者方法处于不同的事务环境中。

为了解决这个问题,你可以通过使用TransactionSynchronizationManager与TransactionAwareProxy来确保主线程与异步线程处于同一事务环境中。

示例代码如下:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Service;
import org.springframework.transaction.support.TransactionSynchronizationManager;
import org.springframework.transaction.support.TransactionTemplate;@Service
@Transactional
public class UserService {private final UserProcessor userProcessor;private final TransactionTemplate transactionTemplate;private final ApplicationContext applicationContext;public UserService(UserProcessor userProcessor, TransactionTemplate transactionTemplate, ApplicationContext applicationContext) {this.userProcessor = userProcessor;this.transactionTemplate = transactionTemplate;this.applicationContext = applicationContext;}public void handleUser(Long userId) {// 在Service方法中调用异步方法User user = userDao.findById(userId);// 对用户数据进行处理// ...transactionTemplate.execute(status -> {// 确保主线程与异步线程处于同一事务环境中boolean asyncTransactionActive = TransactionSynchronizationManager.isActualTransactionActive();return applicationContext.getBean(UserProcessor.class).handleUser(userId, asyncTransactionActive);});userDao.update(user); // 写入用户数据到数据库}}

在这个示例中,我们在UserService类中将TransactionTemplate和ApplicationContext添加到构造函数中,并将UserProcessor的处理方法改为接受一个boolean类型的参数,该参数表示异步线程是否需要在一个活动的事务中执行。在UserService的handleUser()方法中,我们使用TransactionTemplate.execute()方法来确保主线程与异步线程处于同一事务环境中。

通过以上处理,即可保证在使用@Async注解标记的方法中,同时也应用了其他事务注解并能正常执行。


http://www.ppmy.cn/news/80471.html

相关文章

【英语文学导读】课程论文《哈姆雷特》读后感

蓦然回首&#xff0c;尘埃落定 ——读《哈姆雷特》有感 一、戏剧故事梗概 一部作品是否伟大&#xff0c;一部分取决于作者笔下的作品本身&#xff0c;还有一个至关重要的部分便是它的受众&#xff0c;即读者的文本解读&#xff0c;想必“一千个读者心中有一千个哈姆雷特”就是…

Emacs之打开交互log(九十六)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 人生格言&#xff1a; 人生…

解决:在单项目组件里面引入 base.scss/ base.less 等的外部文件不成功的问题

1、问题展示&#xff1a; 其一、问题描述&#xff1a; 在单文件组件里面使用封装在 base.scss 或 base.less 里面的样式用法一直不成功&#xff1b; 其二、代码&#xff1a; // 虽然已经标明了用的是 scss 的语法&#xff0c;但是页面调用 .scss 里的 style 样式还是不成功&a…

十几年老员工提出的五条有效沟通的建议

作为一个职场老手&#xff0c;我深知有效沟通对于职业生涯的重要性。以下是我为职场新人提出的一些关于有效沟通的建议&#xff1a; 倾听对方的意见和建议 在职场上&#xff0c;我们需要与同事、领导或客户进行沟通。在沟通中&#xff0c;我们不仅要表达自己的意见和看法&…

MyBatis-Plus02 条件构造器QueryWrapper、UpdateWrapper、Condition、LambdaQuery用法详解

目录 ①. 条件构造器与常用的接口 ②. QueryWrapper ③. UpdateWrapper ④. Condition——简化代码开发 ⑤. LambdaQueryWrapper ⑥. LambdaUpdateWrapper ①. 条件构造器与常用的接口 ①. Wrapper介绍&#xff1a; 条件构造器&#xff08;Wrapper&#xff09;的作用&…

雷达中的无源和有源的区别

常规雷达探测目标时&#xff0c;需要源源不断地发射无线电波&#xff0c;所以叫有源雷达( active radar)。有源雷达的优点是能自主搜索目标&#xff0c;因为它接收的是自己发射的电磁波&#xff0c;所以灵敏度高&#xff0c;分辨率好。但这种雷达易受目标的电磁干扰&#xff0c…

干货 | 利用SPSS进行高级统计分析第三期

Hello&#xff0c;大家好&#xff01; 这里是壹脑云科研圈&#xff0c;我是喵君姐姐~ 在本期中&#xff0c;我们继续为大家介绍如何利用SPSS进行&#xff1a;单因素方差分析、多因素方差分析、重复测量方差分析等。 1. 单因素方差分析【组间实验单一因变量&#xff1b;进行差…

MyBatis-Plus_04 代码生成器、多数据源(主从)、MyBatisX插件

目录 ①. 代码生成器 ②. 多数据源&#xff08;主从&#xff09; ③. MyBatisX ①. 代码生成器 添加代码生成器依赖 <dependency><groupId>com.baomidou</groupId><artifactId>mybatis-plus-generator</artifactId><version>3.5.1&…