首页 > 编程语言 >JAVA 解析Excel + 多线程 + 事务回滚(2)

JAVA 解析Excel + 多线程 + 事务回滚(2)

时间:2023-11-17 15:22:16浏览次数:35  
标签:JAVA int CountDownLatch Excel private 线程 new import 多线程

该方法为网上查询,感觉可行,并未真正尝试。

主线程:

package com.swagger.demo.service;

import com.alibaba.excel.context.AnalysisContext;
import com.alibaba.excel.event.AnalysisEventListener;
import com.swagger.demo.config.SpringJobBeanFactory;
import com.swagger.demo.mapper.DeadManMapper;
import com.swagger.demo.model.entity.DeadManExcelData;
import com.swagger.demo.thread.DeadManThread;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.*;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* @author zrc
* @version 1.0
* @description: TODO 最新入狱名单导入监听器
*
* @date 2022/5/30 15:56
*/
@Service
@Slf4j
@Component
public class DeadManExcelListener extends AnalysisEventListener<DeadManExcelData> {

/**
* 多线程保存集合,使用线程安全集合
*/
private List<DeadManExcelData> list = Collections.synchronizedList(new ArrayList<>());

/**
* 创建线程池必要参数
*/
private static final int CORE_POOL_SIZE = 10; // 核心线程数
private static final int MAX_POOL_SIZE = 100; // 最大线程数
private static final int QUEUE_CAPACITY = 100; // 队列大小
private static final Long KEEP_ALIVE_TIME = 1L; // 存活时间

public List<DeadManExcelData> getData(){
return list;
}

public DeadManExcelListener(){

}

public void setData(List<DeadManExcelData> deadManExcelDataList){
this.list = deadManExcelDataList;
}

@Override
public void invoke(DeadManExcelData deadManExcelData, AnalysisContext analysisContext) {
if(deadManExcelData!=null){
list.add(deadManExcelData);
}
}

/**
* 多线程方式保存
* @param analysisContext
*/
@Override
public void doAfterAllAnalysed(AnalysisContext analysisContext) {
log.info("解析结束,开始插入数据");

// 创建线程池
ExecutorService executor = new ThreadPoolExecutor(CORE_POOL_SIZE,
MAX_POOL_SIZE,
KEEP_ALIVE_TIME,
TimeUnit.SECONDS,
new ArrayBlockingQueue<>(QUEUE_CAPACITY),
new ThreadPoolExecutor.CallerRunsPolicy());

// 指定每个线程需要处理的导入数量,假设每个线程处理15000条,注意配合上面线程池的大小
int singleThreadDealCount = 15000;

// 根据假设每个线程需要处理的数量以及总数,计算需要提交到线程池的线程数量
int threadSize=(list.size()/singleThreadDealCount)+1;

// 计算需要导入的数据总数,用于拆分时线程需要处理数据时使用
int rowSize = list.size() + 1;

// 测试开始时间
long startTime = System.currentTimeMillis();

// 申明该线程需要处理数据的开始位置
int startPosition = 0;

// 申明该线程需要处理数据的结束位置
int endPosition = 0;

// 为了让每个线程执行完后回到当前线程,使用CountDownLatch,值为线程数,每次线程执行完就会执行countDown方法减1,为0后回到主线程,也就是当前线程执行后续的代码
CountDownLatch count = new CountDownLatch(threadSize);

// 用来控制主线程回到子线程
CountDownLatch mainCount = new CountDownLatch(1);

// 用来控制最终回到主线程
CountDownLatch endCount = new CountDownLatch(threadSize);

// 用来存放子线程的处理结果,若出错就保存一个false
CopyOnWriteArrayList<Boolean> sonResult = new CopyOnWriteArrayList<Boolean>();

// 使用线程安全的对象存储,保存主线程最后总的判断结果,是提交还是回滚
AtomicBoolean ifSubmit = new AtomicBoolean(true);

// 计算每个线程要处理的数据
for(int i=0;i<threadSize;i++){
// 如果是最后一个线程,为保证程序不发生空指针异常,特殊判断结束位置
if((i+1)==threadSize){
// 计算开始位置
startPosition = (i * singleThreadDealCount);
// 当前线程为划分的最后一个线程,则取总数据的最后为此线程的结束位置
endPosition = rowSize-1;
}else{
// 计算开始位置
startPosition = (i * singleThreadDealCount);
// 计算结束位置
endPosition = (i + 1) * singleThreadDealCount;
}

DeadManMapper deadManMapper = SpringJobBeanFactory.getBean(DeadManMapper.class);

DeadManThread thread = new DeadManThread(count,deadManMapper,list,startPosition,endPosition
,sonResult,mainCount,ifSubmit,endCount);
executor.execute(thread);
}
try {
count.await();
for (Boolean resp : sonResult) {
if (!resp) {
// 只要有一个子线程出异常,就设置最终结果为回滚
log.info("主线程:有线程执行失败,所有线程需要回滚");
ifSubmit.set(false);
break;
}
}
} catch (InterruptedException e) {
e.printStackTrace();
}finally {
// 回到子线程处理回滚或者提交事务
mainCount.countDown();
}

try {
endCount.await();
// 逻辑处理完,关闭线程池
executor.shutdown();
long endTime = System.currentTimeMillis();
log.info("总耗时:"+(endTime-startTime));
} catch (InterruptedException e) {
e.printStackTrace();
}
}

}

 

子线程:

package com.swagger.demo.thread;

import com.swagger.demo.config.SpringJobBeanFactory;
import com.swagger.demo.mapper.DeadManMapper;
import com.swagger.demo.model.entity.DeadMan;
import com.swagger.demo.model.entity.DeadManExcelData;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Component;
import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.transaction.support.DefaultTransactionDefinition;

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicBoolean;

/**
* @author zrc
* @version 1.0
* @description TODO
* @date 2022/7/22 15:40
*/
@Component
@Slf4j
public class DeadManThread implements Runnable{

/**
* 当前线程需要处理的总数据中的开始位置
*/
private int startPosition;

/**
* 当前线程需要处理的总数据中的结束位置
*/
private int endPosition;

/**
* 需要处理的未拆分之前的全部数据
*/
private List<DeadManExcelData> list = Collections.synchronizedList(new ArrayList<>());

/**
* 记录子线程第一次执行是否完成
*/
private CountDownLatch count;

private DeadManMapper deadManMapper;

/**
* 保存每个线程的执行结果
*/
private CopyOnWriteArrayList<Boolean> sonResult;

/**
* 记录主线程是否执行过判断每个线程的执行结果这个操作
*/
private CountDownLatch mainCount;

/**
* 记录主线程对每个线程的执行结果的判断
*/
private AtomicBoolean ifSubmit;

/**
* 声明该子线程的事务管理器
*/
private DataSourceTransactionManager dataSourceTransactionManager;

/**
* 声明该线程事务的状态
*/
private TransactionStatus status;

/**
* 记录子线程第二次执行是否完成
*/
private CountDownLatch endCount;

public DeadManThread() {

}

public DeadManThread(CountDownLatch count, DeadManMapper deadManMapper, List<DeadManExcelData> list
, int startPosition, int endPosition, CopyOnWriteArrayList<Boolean> sonResult,CountDownLatch mainCount
,AtomicBoolean ifSubmit,CountDownLatch endCount) {
this.startPosition = startPosition;
this.endPosition = endPosition;
this.deadManMapper = deadManMapper;
this.list = list;
this.count = count;
this.sonResult = sonResult;
this.mainCount = mainCount;
this.ifSubmit = ifSubmit;
this.endCount = endCount;
}

@Override
public void run() {
try{
dataSourceTransactionManager = SpringJobBeanFactory.getBean(DataSourceTransactionManager.class);
DefaultTransactionDefinition def = new DefaultTransactionDefinition();
def.setPropagationBehavior(TransactionDefinition.PROPAGATION_REQUIRES_NEW);
status = dataSourceTransactionManager.getTransaction(def);
if(Thread.currentThread().getName().contains("3")){
throw new RuntimeException("线程3出问题了");
}
List<DeadMan> deadManList = new ArrayList<>();
List<DeadManExcelData> newList = list.subList(startPosition, endPosition);
// 将EasyExcel对象和实体类对象进行一个转换
for (DeadManExcelData deadManExcelData : newList) {
DeadMan deadMan = new DeadMan();
BeanUtils.copyProperties(deadManExcelData, deadMan);
deadManList.add(deadMan);
}
// 批量新增
deadManMapper.insertBatchSomeColumn(deadManList);
sonResult.add(true);
} catch (Exception e) {
e.printStackTrace();
sonResult.add(false);
} finally {
// 当一个线程执行完了计数要减一不然这个线程会被一直挂起
count.countDown();
try {
log.info(Thread.currentThread().getName() + ":准备就绪,等待其他线程结果,判断是否事务提交");
mainCount.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
if (ifSubmit.get()) {
dataSourceTransactionManager.commit(status);
log.info(Thread.currentThread().getName() + ":事务提交");
} else {
dataSourceTransactionManager.rollback(status);
log.info(Thread.currentThread().getName() + ":事务回滚");
}
// 执行完所有逻辑,等待主线程执行
endCount.countDown();
}
}
}

标签:JAVA,int,CountDownLatch,Excel,private,线程,new,import,多线程
From: https://www.cnblogs.com/andy1234/p/17838848.html

相关文章

  • Java实现学生类继承自human类
    编写人类Human,包括私有姓名、性别、年龄,定义获取各个字段的公共方法,再定义公共的构造方法和思考方法。编写继承人类的学生类Student,增加私有的学号字段以及公共的获取学号的方法,还有公共的构造方法、学习方法,并重写toString方法获取学生数据。最后定义测试类,构造若干个学生对象......
  • Java生成随机三维数组图片推流
    生成随机的三维数组frame=np.random.randint(1,254,size=(720,1280,3)).astype(np.uint8)三维数组图片添加时间frame=cv2.putText(frame,datetime.datetime.now().__str__(),(100,100),cv2.FONT_HERSHEY_SIMPLEX,2.0,(255,255......
  • Java代码混淆
    classFinal代码混淆1.参数说明-file加密的jar/war完整路径-packages加密的包名(可为空,多个用","分割)-libjarsjar/war包lib下要加密jar文件名(可为空,多个用","分割)-cfgfiles需要加密的配置文件,一般是classes目......
  • java RestTemplate 发送post请求
    RestTemplate简介RestTemplate是执行HTTP请求的同步阻塞式的客户端,它在HTTP客户端库(如JDKHttpURLConnection,ApacheHttpComponents,okHttp等)基础封装了更加简单易用的模板方法API。即RestTemplate是一个封装,底层的实现还是java应用开发中常用的一些HTTP客户端。相对于直接使用底层......
  • java中的异步任务处理和Feature接口
    简介Java并发包提供了一套框架,大大简化了执行异步任务所需要的开发。框架引入了“执行服务”的概念,封装了任务执行的细节,对任务提交者而言,他可以关注任务本身,如提交任务、获取结果、取消任务。而不用关注任务执行的细节。基本接口①Runnable和Callable:表示要执行的任务②Excecuto......
  • javascript postMessage给子页面发消息
    发送消息页面<!DOCTYPEhtml><html><head><title>demo</title><metacharset="utf-8"/><script>varchildwinconstchildname="popup"functionopenChild(){......
  • java如何做大体积的文件上传和下载
    在Java中,实现大体积文件的上传和下载涉及到处理文件的分片、并发上传、断点续传等问题。本文将详细介绍如何通过Java实现大体积文件的上传和下载。1.文件上传文件上传是将本地文件上传到服务器的过程。对于大体积文件的上传,我们可以将文件分成多个小片段进行并发上传。1.1文件分......
  • 【Java基础】Java中switch的多种写法
    Java中switch的多种写法代码需求:键盘录入一个数字(代表星期几),判断是工作日还是休息日switch最基础写法 publicstaticvoidswitchTest(){while(true){System.out.println("请输入:");Scannersc=newScanner(System.in);......
  • 35个超实用excel快捷键
    以下是一些常用的Excel快捷键,希望对你有所帮助。如果你想要了解更多快捷键,可以参考Excel的官方文档或者在网上搜索相关信息。Ctrl+C:复制选定的单元格或单元格范围。Ctrl+X:剪切选定的单元格或单元格范围。Ctrl+V:粘贴复制或剪切的内容。Ctrl+Z:撤销上一步操作。Ctrl+......
  • Java IO --- select,poll,epoll
    一、select、poll、epollselect、poll、epoll都是IO多路复用的机制且本质上都是同步I/O。IO多路复用就是通过一种机制,可以同时监视多个描述符,一旦某个描述符就绪(一般是读就绪或者写就绪),能够通知进行相应的读写操作。1.1selectintselect(intn,fd_set*readfds,fd_set*writefd......