一、代码示例
List<TestVO> list = new ArrayList<>();
普通for
for(int i=0;i<list.size();i++){
xxxxx
}
for(TestVO vo : list){
xxxx
}
更高效的for循环
list.parallelStream().forEach( vo -> {
xxxxx
});
二、 使用parallelStream循环的优势
使用parallelStream().forEach的方式在处理大数据集和计算密集型任务时,有显著优势,具体如下:
并行处理: parallelStream()会将数据集分割成多个部分,并在多个线程上并行处理。这意味着如果您的机器有多核处理器,每个核心都可以同时处理一部分数据,大大提高了处理速度。
自动线程管理: Java的并行流底层使用了ForkJoinPool,它负责线程的创建、管理和调度。您无需显式地管理线程,这减少了编写并行代码的复杂性和出错的机会。
自动负载均衡: ForkJoinPool会根据处理器核心数量和任务特性动态调整并行度,确保工作均匀分布,从而最大化硬件资源的利用率。
简化代码: 使用parallelStream().forEach可以使代码更简洁,更易于阅读和维护,尤其是当处理逻辑较为复杂时。
可扩展性: 如果将来硬件升级,增加了处理器核心数量,使用parallelStream()的代码可以自动利用新增的核心,而无需修改代码。
隐式的错误处理: 如果并行流中的某个任务抛出了异常,流的执行会停止,异常会被收集并最终抛出,这使得错误处理更加集中和明确。
灵活的中间操作: 在parallelStream()之后,您可以添加各种中间操作(如map, filter, sorted等),然后再进行终端操作(如forEach),这提供了丰富的数据处理能力。
请注意!!并不是所有情况都适用
并行处理也有其局限性和潜在的问题,例如对于小数据集或高度依赖外部资源(如数据库)的操作,使用parallelStream()可能不会带来性能上的提升,甚至可能因为额外的线程开销而减慢速度。因此,在具体场景下,应根据数据量、任务特性和系统资源来权衡是否使用并行流。
三、 使用场景介绍
3.1 使用parallelStream()的情况
大数据集: 当处理的数据量非常大时,使用parallelStream()可以显著加快处理速度,因为数据会被分割到多个线程中并行处理。
计算密集型任务: 如果对每个元素的操作是计算密集型的,即每个元素的处理不需要等待I/O操作(如网络请求或文件读写),那么并行处理可以有效利用多核CPU的计算能力。
无状态或最小状态: 每个并行任务应该尽可能独立,不依赖于共享状态,或者使用原子变量、线程局部变量等机制来最小化状态共享,以减少同步开销。
资源充足: 确保系统资源(如CPU、内存、I/O)足够支持并行处理,特别是当并行操作涉及到外部资源(如数据库连接)时,要确保这些资源能够承受高并发请求。
切忌在这种循环里面进行读取或者更新数据库的操作,经测试,使用小数据集,多数据源,使用MyBatisPlus在此循环中进行数据查询操作,会报表不存在错误,本身就不应该在for循环里访问数据库,但是如果你必须要这么做,请使用普通for循环
3.2 使用普通for循环的情况
小数据集: 对于小数据集,使用for循环可能更加简单且高效,因为并行处理引入的线程调度和同步开销可能超过并行带来的好处。
I/O密集型任务: 如果对每个元素的操作主要是I/O密集型的,比如频繁的数据库查询或网络调用,那么并行处理可能不会带来性能提升,反而可能因为资源争用而降低效率。
复杂的状态管理: 如果每个任务需要访问共享状态,或者任务间存在复杂的依赖关系,使用for循环可能更容易控制和调试。
调试和维护: for循环的代码通常更易于理解和调试,特别是在涉及复杂业务逻辑的情况下。
总结:
对于大规模数据集和计算密集型任务,优先考虑使用parallelStream()。
对于小规模数据集、I/O密集型任务或有复杂状态管理需求的任务,使用普通for循环可能更合适。
在实际应用中,可以通过性能测试来评估不同方法的效率,选择最适合当前场景的方案。