Stream并行流
1.什么是Stream并行流
Stream并行流是Java 8引入的一种并行计算模型,它允许对集合类型数据进行高效的并行处理。底层实现使用了多线程技术,利用多个处理线程同时对数据进行处理,以加快计算速度。
在使用并行流时,数据流会被分成多个独立的子流,并行执行处理操作。每个子流都会被分配到不同的线程上,同时进行处理。最终,处理结果会被合并为一个整体结果。并行流的实现基于Fork/Join框架,它是Java中用于处理递归型分治任务的标准框架。Fork/Join框架使用了一种递归的分治策略,将任务划分为多个小任务,然后并行执行这些小任务,并最终将结果合并起来。在并行流的实现中,数据会被分割成多个数据块,每个数据块会被分配给一个线程进行处理。每个线程只负责处理自己分配到的数据块,避免了线程之间的竞争和同步操作。
2.用法
使用并行流时,可以通过使用.parallel()方法将顺序流转换为并行流。例如,对一个List进行操作,可以使用list.stream().parallel()来创建并行流或者list.parallelStream()来创建并行流。
ist.stream().parallel()和
list.parallelStream()`都是用于将一个List转换成并行流的方法,它们的作用是相同的。
然而,它们的主要区别在于语法和可读性。stream().parallel()
是在List实例上调用stream()方法后再调用parallel()方法,而parallelStream()
是直接在List实例上调用parallelStream()方法。这个区别主要是出于语言设计的考虑,让代码更易读和易懂。parallelStream()
的写法更简洁,直接表达了意图——将List转换成并行流。而stream().parallel()
稍微冗长一些,需要额外的方法调用。实际上,这两种写法在功能和性能上是相同的,它们都可以将List转换为并行流,并且在并行处理数据时,使用相同的底层实现机制。
以下是两种写法的示例代码:
import java.util.ArrayList;
import java.util.List;
public class ParallelStreamExample {
public static void main(String[] args) {
List<Integer> numbers = new ArrayList<>();
numbers.add(1);
numbers.add(2);
numbers.add(3);
numbers.add(4);
numbers.add(5);
// 使用stream().parallel()
numbers.stream().parallel()
.forEach(number -> {
System.out.println("Processing number: " + number);
});
// 使用parallelStream()
numbers.parallelStream()
.forEach(number -> {
System.out.println("Processing number: " + number);
});
}
}
3.场景
外部List文件数据,要在通过文件上传接口上传到本地服务器,并获取文件地址.原写法,forEach遍历后调用上传接口,速度很慢,修改为并行流后多线程下载,速度有所提高.
public APIResult<List<Attach>> transferFiles(@RequestBody List<Attach> attaches) throws IOException {
APIResult<List<Attach>> result = new APIResult<>();
ArrayList<Attach> newAttaches = new ArrayList<>();
HttpClientBuilder httpClientBuilder = HttpClientBuilder.create();
RequestConfig requestConfig = RequestConfig.custom().build();
CloseableHttpClient httpClient = httpClientBuilder.setDefaultRequestConfig(requestConfig).build();
//遍历传入文件List
for (ContractAttach attache : attaches) {
//...处理数据
MultipartFile newFile = attache.get();
//...调用上传文件
APIResult<AttachmentPo> attachmentResult = iUploadService.upload(newFile);
newAttaches.add(attachmentResult.getData);
}
return result;
}
//并行
attaches.parallelStream()
.forEach(attache -> {
//...处理数据
MultipartFile newFile = attache.get();
//...调用上传文件
APIResult<AttachmentPo> attachmentResult = iUploadService.upload(newFile);
newAttaches.add(attachmentResult.getData);
};
4.注意
在使用stream.foreach时这个遍历没有线程安全问题,但是使用parallelStream就会有线程安全问题,所有在parallelStream里面使用的外部变量,比如集合一定要使用线程安全集合,不然就会引发多线程安全问题。
需要注意的是,并行流的效果取决于具体的应用场景和硬件资源。在某些情况下,并行流可以显著提高程序的性能,但在其他情况下,它可能会导致性能下降。因此,在使用并行流时,建议进行性能测试和调优,以确定是否使用并行流以及如何使用它。