Java多线程导出Excel示例

在之前的Java多线程导入Excel示例中演示了如何通过多线程的方式导入Excel,下面我们再来看下怎么通过多线程的方式导出Excel
还是直接上代码
首先是Controller

import com.sakura.base.service.ExcelService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.*;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

@RestController
@RequestMapping("/excel")
public class ExcelController {

    @Autowired
    private ExcelService excelService;

    @GetMapping("/export")
    public void exportExcel(HttpServletRequest request, HttpServletResponse response) throws Exception {
        excelService.exportExcel(request, response);
    }
}

然后Service

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

public interface ExcelService {

    void exportExcel(HttpServletRequest request, HttpServletResponse response) throws Exception;

}

还有ServiceImpl

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.sakura.base.entity.User;
import com.sakura.base.mapper.UserMapper;
import com.sakura.base.service.ExcelService;
import lombok.extern.java.Log;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.util.Date;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;

@Service
@Log
public class ExcelServiceImpl implements ExcelService {

    @Autowired
    private UserMapper userMapper;

    @Override
    public void exportExcel(HttpServletRequest request, HttpServletResponse response) throws Exception {
        // 创建一个Excel文件,放在这里可以不用手动关闭流
        try (Workbook workbook = new XSSFWorkbook()) {
            // 创建一个Excel页
            Sheet sheet = workbook.createSheet("users");
            // 创建表头
            Row headerRow = sheet.createRow(0);
            // 给表头创建单元格并赋值
            headerRow.createCell(0).setCellValue("姓名");
            headerRow.createCell(1).setCellValue("手机号");
            headerRow.createCell(2).setCellValue("导出日期");

            // 每次处理的数据量,大家可以自己调整,比如每次处理1000条
            int batchSize = 5;
            // 最大线程数,大家可以自己调整,根据自己服务器性能来调整
            int maxThreads = 3;

            // 创建线程池
            ExecutorService executorService = Executors.newFixedThreadPool(maxThreads);

            // 查询总记录数
            int totalCount = userMapper.selectCount(null);
            // 计算总批次
            int totalBatches = (int) Math.ceil((double) totalCount / batchSize);

            // 分批次导出数据
            for (int i = 0; i < totalBatches; i++) {
                int offset = i * batchSize;
                // 提交任务
                executorService.execute(new ExcelWriterTask(sheet, offset, batchSize));
            }

            // 关闭线程池
            executorService.shutdown();
            executorService.awaitTermination(Long.MAX_VALUE, TimeUnit.SECONDS); // 等待所有任务执行完毕 Long.MAX_VALUE为超时时间,可以自由设置

            response.setContentType("application/vnd.ms-excel");
            response.setHeader("Content-disposition", "attachment;filename=users.xlsx");
            workbook.write(response.getOutputStream());
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    private class ExcelWriterTask implements Runnable {
        private Sheet sheet;
        private int offset;
        private int batchSize;

        public ExcelWriterTask(Sheet sheet, int offset, int batchSize) {
            this.sheet = sheet;
            this.offset = offset;
            this.batchSize = batchSize;
        }

        @Override
        public void run() {

            // 测试用,模拟处理数据的耗时
            // _________________________________________
            try {
                Thread.sleep(2000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
            // _________________________________________

            // 查询数据
            List<User> users = userMapper.selectList(new QueryWrapper<User>().orderByAsc("id").last("limit " + offset + ", " + batchSize));
            // 这里要注意,写入数据的时候需要同步写入,不然会出现覆盖等问题
            synchronized (sheet) {
                int rowNum = sheet.getLastRowNum();
                for (User user : users) {
                    Row row = sheet.createRow(++rowNum);
                    row.createCell(0).setCellValue(user.getName());
                    row.createCell(1).setCellValue(user.getPhoneNumber());
                    // 加个日期是为了好验证多线程导出的结构
                    row.createCell(2).setCellValue(new Date().toString());
                }
            }
        }
    }

}

实体类User这里也不贴了
poi的jar包

		<dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi</artifactId>
            <version>3.15</version>
        </dependency>

        <dependency>
            <groupId>org.apache.poi</groupId>
            <artifactId>poi-ooxml</artifactId>
            <version>3.15</version>
        </dependency>

测试一下接口

在这里插入图片描述

看下导出的Excel里面的数据,一样的,因为我限制了每次处理的数据为5条,同时最多有3个线程,所以可以看到同一时间段导出的数据为15条

在这里插入图片描述

这里也有个问题,细心的人可能已经发现了,在写入的时候其实我加了synchronized关键字,这也导致了所有线程都必须等待其他线程完成写入操作后才能进行自己的写入操作,从而将并发写入转变为了串行写入,这似乎与多线程的使用相违背,这这种想法是对的,但是目前我也没想到更好的方法来控制多个线程同时写入的问题,当然这样写还是有用上多线程加快处理速度的特性的,毕竟在数据处理阶段,本来一条数据就要处理2秒,现在3个线程2秒可以同时处理3条数据,至于写入的那点时间其实可以忽略不计。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/422434.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

【数据分享】2000~2023年MOD15A2H 061 光合有效辐射分数FPAR数据集

​各位同学们好&#xff0c;今天和大伙儿分享的是2000~2023年MOD15A2H 061 光合有效辐射分数FPAR数据集。如果大家有下载处理数据等方面的问题&#xff0c;可以评论或私信。 Myneni, R., Y. Knyazikhin, T. Park. MODIS/Terra Leaf Area Index/FPAR 8-Day L4 Global 500m SIN G…

网络工程师笔记6

ICMP协议 Internet控制报文协议ICMP(InternetControlMessage Protocol)是网络层的一个重要协议。ICMP协议用来在网络设备间传递各种差错和控制信息&#xff0c;它对于收集各种网络信息、诊断和排除各种网络故障具有至关重要的作用。使用基于ICMP的应用时&#xff0c;需要对ICMP…

live555源码学习(1)

1 基础组件 live项目主要包含了四个基础库、程序入口类&#xff08;mediaServer&#xff09;和测试程序&#xff08;testProgs&#xff09;。四个基础库是UsageEnvironment、BasicUsageEnvironment、groupsock和liveMedia UsageEnvironment 抽象了两个类UsageEnvironment和T…

基于YOLOv8/YOLOv7/YOLOv6/YOLOv5的钢材表面缺陷检测系统(Python+PySide6界面+训练代码)

摘要&#xff1a;开发钢材表面缺陷检测系统对于保障制造质量和提高生产效率具有关键作用。本篇博客详细介绍了如何运用深度学习构建一个钢材表面缺陷检测系统&#xff0c;并提供了完整的实现代码。该系统基于强大的YOLOv8算法&#xff0c;并对比了YOLOv7、YOLOv6、YOLOv5&#…

Grid-Based Continuous Normal Representation for Anomaly Detection 论文阅读

Grid-Based Continuous Normal Representation for Anomaly Detection 论文阅读 摘要简介方法3.1 Normal Representation3.2 Feature Refinement3.3 Training and Inference 4 实验结果5 总结 文章信息&#xff1a; 原文链接&#xff1a;https://arxiv.org/abs/2402.18293 源码…

应用层DDoS防护:理解、必要性与实现策略

一、应用层简介 应用层&#xff0c;也称作第七层&#xff0c;是OSI&#xff08;开放系统互联&#xff09;模型中的最高层。在这一层&#xff0c;数据以特定的应用程序协议格式进行传输&#xff0c;如HTTP、FTP、SMTP等。应用层的主要职责是为用户提供网络服务&#xff0c;如文…

Android Gradle开发与应用 (四) : Gradle构建与生命周期

1. 前言 前几篇文章&#xff0c;我们对Gradle中的基本知识&#xff0c;包括Gradle项目结构、Gradle Wrapper、GradleUserHome、Groovy基础语法、Groovy语法概念、Groovy闭包等知识点&#xff0c;这篇文章我们接着来介绍Gradle构建过程中的知识点。 2. Project : Gradle中构建…

python61-Python的循环之for-in循环遍历列表和元组

在使用 for-in 循环遍历列表和元组时&#xff0c;列表或元组有几个元素&#xff0c;for-in 循环的循环体就执行几次&#xff0c;针对每个元素执行一次&#xff0c;循环计数器会依次被赋值为元素的值&#xff0c;如下代码使用 for-in 循环遍历元组。 # !/usr/bin/env python# -…

C# Socket通信从入门到精通(21)——TCP发送文件与接收文件 C#代码实现

1、前言 我们在开发上位机软件的过程中经常需要发送文件,本文就是介绍如何利用tcp客户端发送文件、tcp服务器端接收文件,而且本文介绍的方法可以自动发送一个文件夹下的所有子目录以及所有文件,经验来自于实际项目,具备非常有价值的参考意义! 2、发送文件以及C#代码 被发…

基于React俄罗斯方块h5小游戏源码响应式支持PC+手机

俄罗斯方块是一款广受欢迎的经典游戏&#xff0c;许多编程语言都热衷于实现它。在JavaScript中&#xff0c;也有许多版本。 我的目标是使用React框架来实现这个游戏。 地 址 &#xff1a; runruncode.com/vue/19701.html 游戏的架构采用了React和Redux&#xff0c;为了提高性…

php源码 单色bmp图片取模工具 按任意方式取模 生成字节数组 自由编辑点阵

http://2.wjsou.com/BMP/index.html 想试试chatGPT4生成&#xff0c;还是要手工改 php 写一个网页界面上可以选择一张bmp图片&#xff0c;界面上就显示这张bmp图片&#xff0c; 点生成取模按钮&#xff0c;在图片下方会显示这张bmp图片的取模数据。 取模规则是按界面设置的&a…

Pegasus智能家居套件样例开发--软定时器

样例简介 此样例将演示如何在Pegasus Wi-Fi IoT智能家居套件上使用cmsis 2.0 接口进行定时器开发。 工程版本 系统版本/API版本&#xff1a;OpenHarmony 3.0 releaseIDE版本&#xff1a;DevEco Device Tool Release 3.0.0.401 快速上手 准备硬件环境 预装windows系统的PC…

指针与malloc动态内存申请,堆和栈的差异

定义了两个函数print_stack()和print_malloc()&#xff0c;分别演示了两种不同的内存分配方式&#xff1a;栈内存和堆内存。然后在main()函数中调用这两个函数&#xff0c;并将它们返回的指针打印出来。 由于print_stack()中的数组c是在栈上分配的&#xff0c;当函数返回后&…

Matlab|考虑源-荷-储协同互动的主动配电网优化调度研究

目录 主要内容 部分代码 结果一览 主要内容 该程序以33节点系统为例实现了考虑源-荷-储协同互动的主动配电网优化调度模型&#xff0c;程序采用配电网二阶锥约束、储能约束、分布式电源约束、可平移、可削减负荷约束等&#xff0c;以负荷调用成本、储能调用成本、…

USB4之ASM2464PD与ASM2464PDX兼容与运用

首先在NVMe上运用: 一&#xff1a;ASM2464PD&#xff08;现在可以做带PD的方案&#xff09; 二&#xff1a;ASM2464PDX 1&#xff1a; Application Guide- CFX card reader NVMe SSD 2&#xff1a;ASM2464PDX Application Guide- NVMe SSD x4 with data clone 三&#xff…

并查集(Disjoint Set)

目录 1.定义 2.初始化 3.查找 4.合并 4.1.按秩合并&#xff08;启发式合并&#xff09; 5.例题 题目描述 输入格式 输出格式 输入输出样例 说明/提示 1.定义 并查集&#xff0c;也称为不相交集合数据结构&#xff0c;是一种用于管理元素分组以及查找元素所属组的数…

回溯 Leetcode 47 全排列II

全排列II 给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列。 Leetcode 47 学习记录自代码随想录 示例 1&#xff1a; 输入&#xff1a;nums [1,1,2] 输出&#xff1a; [[1,1,2], [1,2,1], [2,1,1]] 示例 2&#xff1a; 输入&#xff1…

pd sink取电协议芯片介绍

前言&#xff1a; 在如今快节奏生活不断蔓延的背景下&#xff0c;人们对各种事情的处理也渐渐地开始要求在保证质量的情况下&#xff0c;不断加快。手机快充就是一个典型的例子&#xff0c;从开始的18W&#xff0c;30W快充&#xff0c;到现在已经有240W的超级快充出现。在这其…

ICLR 2024|ReLU激活函数的反击,稀疏性仍然是提升LLM效率的利器

论文题目&#xff1a; ReLU Strikes Back: Exploiting Activation Sparsity in Large Language Models 论文链接&#xff1a; https://arxiv.org/abs/2310.04564 参数规模超过十亿&#xff08;1B&#xff09;的大型语言模型&#xff08;LLM&#xff09;已经彻底改变了现阶段人工…

tritonserver学习之八:redis_caches实践

tritonserver学习之一&#xff1a;triton使用流程 tritonserver学习之二&#xff1a;tritonserver编译 tritonserver学习之三&#xff1a;tritonserver运行流程 tritonserver学习之四&#xff1a;命令行解析 tritonserver学习之五&#xff1a;backend实现机制 tritonserv…