目录
全栈码农以及毕业设计实战开发,CSDN平台Java领域新星创作者,专注于大学生项目实战开发、讲解和毕业答疑辅导。
一、前言
在Spring Boot中处理多线程事务时,需要注意事务的隔离性和线程安全性。由于Spring的默认事务管理机制(如通过@Transactional
注解)是基于ThreadLocal的,它确保事务的上下文(如数据库连接和Session)与当前线程绑定。然而,当在事务内部启动新线程时,这些上下文信息并不会被新线程继承,这可能导致事务失效、数据不一致或死锁等问题。
二、解决方案
-
单线程优化:首先,尽量通过优化单线程逻辑来解决问题,如减少数据库交互次数、优化查询语句等。
-
合理分块后多线程处理:对于大批量数据处理,可以将其分成多个小块,每个小块在独立线程中处理,并使用类似分布式事务的方式确保数据的最终一致性。
-
自定义事务管理:在某些情况下,可能需要编写自定义的事务管理逻辑来适应多线程环境。
三、代码示例
实体类定义:
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.Id;
import javax.persistence.Table;
import java.io.Serializable;
import java.math.BigDecimal;
@Data
@Entity
@Table(name = "stu")
@AllArgsConstructor
@NoArgsConstructor
public class Stu implements Serializable {
private static final long serialVersionUID = 1L;
@Id
@Column(name = "id", nullable = false)
private Integer id;
@Column(name = "name", nullable = false)
private String name;
@Column(name = "price")
private BigDecimal price;
public Stu(String name, BigDecimal price) {
this.name = name;
this.price = price;
}
}
服务层处理:
import com.example.demo.threadTransactional.entity.Stu;
import com.example.demo.threadTransactional.repository.StuRepository;
import com.google.common.collect.Lists;
import com.google.common.util.concurrent.ThreadFactoryBuilder;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.List;
import java.util.concurrent.*;
@Service
@Slf4j
public class StuService {
@Autowired
private StuRepository stuRepository;
// 假设的异步方法,用于处理分块数据
@Async
@Transactional
public void saveBatch(List<Stu> stuList) {
for (Stu stu : stuList) {
stuRepository.save(stu);
}
}
// 主方法,用于分割数据并启动多线程
public void saveBooks(List<Stu> bookList) throws InterruptedException {
// 根据业务场景,将大任务进行划分成小任务
List<List<Stu>> partition = Lists.partition(bookList, 100); // 每批100个对象
// 线程池
ExecutorService executor = Executors.newFixedThreadPool(4); // 4个线程
// 启动多个线程处理
List<Future<?>> futures = new ArrayList<>();
for (List<Stu> part : partition) {
Future<?> future = executor.submit(() -> saveBatch(part));
futures.add(future);
}
// 等待所有线程完成
for (Future<?> future : futures) {
future.get(); // 阻塞直到该任务完成
}
executor.shutdown();
executor.awaitTermination(1, TimeUnit.HOURS); // 等待所有任务完成
}
}
四、注意事项
- 这里的
@Transactional
注解用于每个saveBatch
方法,但在多线程环境下,它实际上是对每个线程独立生效的,而不是跨线程的事务。 - 使用了
@Async
注解来标记saveBatch
方法为异步执行,确保它在不同线程中运行。 - 使用了线程池来管理多个线程,避免频繁创建和销毁线程的开销。
- 使用了`Future.