BlockingQueue与Thread Pool-切分百万数据秒写Excel表

文章讲述了如何使用LinkedBlockingQueue动态切分大量数据,并通过固定线程池实现多页同步写入Excel,以及测试过程中时间和性能的分析。作者指出,通过进一步优化可以提高写表速度。
摘要由CSDN通过智能技术生成

切分数据

按指定的 pieces 段数, 将数据切分, 并送入队列.

private final LinkedBlockingQueue<List<User>> usersQueue = new LinkedBlockingQueue<>();

private void feedSubUsers(ArrayList<User> users, int pieces) throws InterruptedException {
        int size = users.size();
        int step = Math.floorDiv(size, pieces);
        int topIndex = 0;
        int endIndex = 0;
        int track = 0;
        // the loop condition of (endIndex < size) will pick up the rest elements
        while (track < pieces || endIndex < size) {
            if (endIndex == 0) {
                endIndex = topIndex + step;
            } else if ((size - endIndex) > step) {
                topIndex = endIndex;
                endIndex = endIndex + step;
            } else if ((size - endIndex) > 0 && (size - endIndex) <= step) {
                // size - endIndex <= step
                topIndex = endIndex;
                endIndex = size;
            }
            usersQueue.put(users.subList(topIndex, endIndex));
            track++;
        }
    }

队列与线程

我们使用 LinkedBlockingList 作为任务队列, 因为数据集切分是动态的, 所以用 linked. 切分后将数据集作为一个任务添加进队列, 等待全部切分完毕后才开始线程池提交任务, 并开始执行数据集的表写入操作. 这里的队列还只是作为任务队列, 并不是完全动态的 cons-prod 模式. 因为任务已经提前生产出来, 且已完备. 然后线程池中的线程根据队列中的任务数量来进行消费. ( 由于我们用的是 FixedThreadPool, 所以如果任务数超过线程数, 不会增多线程, 当有空闲线程时就会去消费, 如果任务数小于线程数, 那么每个线程消费一个任务)

多页同时写入

  1. 使用 EasyExcel 多页写入时, 要对 excelWriter 进行同步, 防止写入错误.
  2. 这里使用 Thread-No 线程号来作为 sheet 分页的名字.
  3. 如果固定线程池的线程数少于分页数的话, 会将新数据以 append 形式同页写入, 而不会覆盖.
  4. 整体代码中还可以优化的地方.
private final ExecutorService executorService = Executors.newFixedThreadPool(10);

public void operateOnExcel(ArrayList<User> users) throws InterruptedException, ExecutionException {
        ExcelWriter excelWriter = EasyExcel.write("/Users/wong/Desktop/helloworld.xlsx", User.class).build();
        // separating List and feed sublist to queue
        try {
            feedSubUsers(users, 20);
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        }
        ArrayList<Future<Boolean>> futures = new ArrayList<>();
        // get the sublist from the queue and wrap it into task
        Callable<Boolean> task = () -> {
            WriteSheet sheet = EasyExcel.writerSheet(String.valueOf(Thread.currentThread().getId())).build();
            synchronized (excelWriter) {
                excelWriter.write(usersQueue.poll(), sheet);
            }
            return true;
        };
        Instant begin = Instant.now();
        // executing tasks
        for (int i = 0; i < usersQueue.size(); i++) {
            Future<Boolean> submit = executorService.submit(task);
            futures.add(submit);
        }
        for (var future : futures) {
            future.get();
        }
        Instant end = Instant.now();
        // close workbook io
        excelWriter.finish();
        executorService.shutdown();
        System.out.println("Writing WorkBook Time Consuming: " + Duration.between(begin, end).toMillis() + " MS");
    }

测试结果

/**
 * Hello world!
 */
public class App {
    public static void main(String[] args) throws ExecutionException, InterruptedException, IOException {
        Operators operators = new Operators();
        ArrayList<User> users = operators.yieldUser(1000000);
        // operators.testSheet(users);
        operators.operateOnExcel(users);
    }
}

在这里插入图片描述
整体来说, 生成百万个Users花费5秒, 写表花了7秒. 两个阶段是 synchronous. 所以总共花了12秒. 如果用完全形态的 cons-prod 模式, 还可以更快…单论写表操作, 貌似还可以使用其他写策略来提高速度.

  • 9
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值