java8 stream并发处理分批操作

该博客探讨了在大量数据查询时,使用Java8的Stream并行处理相比于传统分批查询的性能优势。通过示例代码展示了如何利用Stream进行并发查询,结果显示并行处理能显著减少总耗时。文章还提到了使用ExecutorService实现类似功能,并提供了性能比较的参考链接。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

使用场景:

查询数据量很大的时候需要分批查询

在不并发操作的情况下,假设数据总量为 10万,每次查询500条数据,每次查询耗时100ms,则总耗时最少要20s,这样是相当慢的

 

使用java8的stream可以多线程查询数据

两者耗时对比如下

​
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * @desc  java8 流的方式并发处理分批操作
 * @date 2021/1/6
 */
public class ParallelTest {
    public static void main(String[] args) {

        //业务数据
        List<String>  dataList=new ArrayList<>();
        for (int i = 0; i < 100000; i++) {
            dataList.add(i+"");
        }
        int num=500;
        long startTime = System.currentTimeMillis();
        List<String> result=new ArrayList<>();
        try {
            result = Stream.iterate(0, i -> i + 1).limit(dataList.size() / num + 1).parallel().map(i -> {
                //获取每一批的数据
                List<String> perList = dataList.parallelStream().skip(i * num)
                        .limit(num)
                        .collect(Collectors.toList());
                //业务处理
              return   queryThisData(perList,i);
            }).filter(o -> Objects.nonNull(o))
                    .flatMap(Collection::parallelStream)
                    .collect(Collectors.toList());
        } catch (Exception e) {
            e.printStackTrace();
        }

        long endTime = System.currentTimeMillis();
        System.out.println("并发批量处理耗时:"+(endTime-startTime)+" 数据总数:"+result.size());

        //不并发分批
        List<String> result2=new ArrayList<>();
        List<List<String>> parList=new ArrayList<>();
        int size = dataList.size() % num == 0 ? (dataList.size() / num) : (dataList.size() / num + 1);
        for (int i = 0; i < size ; i++) {
            List<String> list=new ArrayList<>();
              for (int j=i*num;j<num*(i+1);j++){
                  list.add(dataList.get(j));
              }
            parList.add(list);
        }
        for (int i = 0; i < parList.size(); i++) {
            result2.addAll(queryThisData(parList.get(i),i));
        }
        System.out.println("不并发批量处理耗时:"+(System.currentTimeMillis()-endTime)+" 数据总数:"+result2.size());

    }

    private static List<String> queryThisData(List<String> perList,int i) {
        try {
            Thread.sleep(100);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        return perList;
    }

}

​

输出结果:

并发批量处理耗时:2784 数据总数:100000
不并发批量处理耗时:20122 数据总数:100000

也可以用ExecutorService实现

两者性能比较参考:

https://www.cnblogs.com/metoy/p/4323910.html

 

### Java 中的数据分批处理 #### 使用简单的循环机制进行分批处理 对于较小规模的数据集,可以通过遍历列表并按固定大小分割成多个子列表来实现分批处理。这种方法简单直观,适用于大多数场景。 ```java import java.util.ArrayList; import java.util.List; public class BatchProcessor { public static void main(String[] args) { List<Integer> list = new ArrayList<>(); // 假设这里填充了大量的整数数据到list中 int batchSize = 1000; // 设置每批次的数量 for (int i = 0; i < list.size(); i += batchSize) { System.out.println("======================进行第" + ((i / batchSize)+1) + "次批处理======================="); int endIndex = Math.min(i + batchSize, list.size()); List<Integer> sublist = list.subList(i, endIndex); processBatch(sublist); // 调用具体的业务逻辑函数 } } private static void processBatch(List<Integer> batch){ // 批量处理的具体操作 } } ``` 此代码展示了如何利用`subList()`方法获取原列表的一个视图,并对其进行独立的操作[^1]。 #### 利用流式API(Stream API)简化分批流程 自Java8引入了强大的Stream API之后,在某些情况下也可以借助它来进行更简洁优雅的分页处理: ```java import java.util.stream.Collectors; import java.util.stream.IntStream; // ...其他部分保持不变... IntStream.range(0, (list.size() + batchSize - 1)/batchSize).forEach(batchIndex ->{ System.out.println("======================进行第"+ (batchIndex+1)+"次批处理======================="); int start = batchIndex * batchSize; int end = Math.min(start + batchSize, list.size()); List<Integer> currentBatch = IntStream.rangeClosed(start,end-1) .mapToObj(list::get) .collect(Collectors.toList()); processBatch(currentBatch); }); ``` 这段代码使用了`range()`和`mapToObj()`等函数组合起来创建了一个范围内的索引序列,进而映射回原始列表中的元素形成新的批次[^2]。 #### 结合多线程技术提升并发性能 当面对海量级的数据时,单一线程可能无法满足实时性的要求。此时可以考虑采用多线程的方式加速整个过程。下面是一个基于ExecutorService框架的例子: ```java import java.util.concurrent.*; //...省略前面相同的部分... private final ExecutorService executor = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors()); protected Future<?> submitTask(Callable<Void> task){ return executor.submit(task); } void parallelProcessing(){ try { List<Future<?>> futures = new CopyOnWriteArrayList<>(); IntStream.range(0,(list.size()+batchSize-1)/batchSize).forEach(batchIndex->{ Callable<Void> callable=()->{ int startIndex=batchIndex*batchSize; int stopIndex=Math.min((startIndex+batchSize),list.size()); List<Integer> currBatch=list.subList(startIndex,stopIndex); synchronized(this){ System.out.println("======================正在进行第"+(batchIndex+1)+"次批处理======================="); } processBatch(currBatch); return null; }; futures.add(submitTask(callable)); }); CompletableFuture.allOf(futures.toArray(new Future[0])).join(); } finally { shutdownAndAwaitTermination(executor); } } private void shutdownAndAwaitTermination(ExecutorService pool){ pool.shutdown(); try { if (!pool.awaitTermination(60, TimeUnit.SECONDS)) { pool.shutdownNow(); } } catch (InterruptedException e) { pool.shutdownNow(); } } ``` 上述实现了通过提交一系列异步任务给线程池执行的方式来完成大批量数据的高效处理工作[^4]。
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值