Java 使用线程池和CountDownLatch分批插入或者更新数据
需求:在开发业务报表时,需要从MySQL数据库读取数据后进行操作,然后写入数据库,使用定时任务跑批。
分析:①兼顾性能,② MySQL没有Oracle那么方便、强大的存储过程。综上所述,使用线程池以分批提交的方案把数据插入MySQL数据库,对于更新操作、在Java端进行分页等的操作,本方案也支持。代码如下:
/**
* 分页操作数据
*/
@Slf4j
@Component
public class BatchDealDemo {
private void batchDeal(List data, int batchNum) throws InterruptedException {
int totalNum = data.size();
int pageNum = totalNum % batchNum == 0 ? totalNum / batchNum : totalNum / batchNum + 1;
ExecutorService executor = Executors.newFixedThreadPool(pageNum);
log.info("主线程开始执行 begin --- ");
try {
CountDownLatch countDownLatch = new CountDownLatch(pageNum);
List subData = null;
int fromIndex, toIndex;
for (int i = 0; i < pageNum; i++) {
fromIndex = i * batchNum;
toIndex = Math.min(totalNum, fromIndex + batchNum);
subData = data.subList(fromIndex, toIndex);
ImportTask task = new ImportTask(subData, countDownLatch);
executor.execute(task);
}
// 当前线程必须在执行完任务之后立即调用CountDownLatch.await()方法,
// 这样主线程的操作就会被阻塞,直到全部子线程完成各自的任务。
// CountDownLatch类中计数器的值等于0时,主线程就能通过await()方法恢复执行自己的任务。
countDownLatch.await();
log.info("数据操作完成!可以在此开始主线程的其它业务");
log.info("主线程执行完毕 end --- ");
} finally {
// 关闭线程池,释放资源
executor.shutdown();
}
}
class ImportTask implements Runnable {
private List list;
private CountDownLatch countDownLatch;
public ImportTask(List data, CountDownLatch countDownLatch) {
this.list = data;
this.countDownLatch = countDownLatch;
}
@Override
public void run() {
if (null != list) {
// 业务逻辑,例如批量insert或者update
log.info("现在操作的数据是{}", list);
}
// 发出子线程任务完成的信号
countDownLatch.countDown();
}
}
}
在第1行中,List data表示传入的数据,batchNum表示每一次处理的数量,例如500条等。
读后有收获,小礼物走一走,请作者喝咖啡。
Buy me a coffee. ☕Get red packets.
作者:楼兰胡杨
本文版权归作者和博客园共有,欢迎转载,但请注明原文链接,并保留此段声明,否则保留追究法律责任的权利。