并行流可能导致性能下降的情况
- 数据量小:对于小数据集,并行处理的线程创建、调度和销毁开销可能超过实际处理时间,导致性能下降。
- 任务粒度小:如果每个任务的计算量非常小,线程间的协调和通信开销将占据较大比例,降低并行处理的优势。
- 顺序依赖:当流中的操作依赖于元素的顺序,并行流需要额外的同步机制来维护顺序,这会增加开销,降低性能。
- 共享可变状态:在并行流操作中,如果多个线程访问和修改共享可变状态,会导致线程安全问题,需要额外的同步操作,从而降低性能。
避免性能下降的方法
- 大数据集使用:确保数据集足够大,使得并行处理的优势能够体现出来。一般来说,当数据集元素数量超过几千个时,并行流更有可能提高性能。
- 增大任务粒度:将多个小任务合并为较大的任务,减少线程间的协调开销。例如,可以使用
Collectors.groupingBy
等方法对数据进行分组后再处理。
- 避免顺序依赖:如果可能,尽量将操作设计为无顺序依赖的。如果必须保持顺序,可以考虑先并行处理,然后再对结果进行排序。
- 避免共享可变状态:使用不可变对象或线程安全的数据结构,避免多个线程对共享状态的竞争。
对包含大量复杂对象的集合进行复杂计算后汇总的正确做法
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
class ComplexObject {
private int prop1;
private double prop2;
// 其他属性和getter、setter方法
public ComplexObject(int prop1, double prop2) {
this.prop1 = prop1;
this.prop2 = prop2;
}
public int getProp1() {
return prop1;
}
public double getProp2() {
return prop2;
}
}
public class ParallelStreamExample {
public static void main(String[] args) {
List<ComplexObject> complexObjectList = new ArrayList<>();
// 填充大量复杂对象
for (int i = 0; i < 1000000; i++) {
complexObjectList.add(new ComplexObject(i, Math.random()));
}
// 并行流处理多个属性的复杂计算并汇总
double result = complexObjectList.parallelStream()
.mapToDouble(obj -> {
// 复杂计算示例
double calculation1 = obj.getProp1() * obj.getProp2();
double calculation2 = Math.sqrt(obj.getProp2());
return calculation1 + calculation2;
})
.sum();
System.out.println("汇总结果: " + result);
}
}
- 使用
parallelStream
:将集合转换为并行流,充分利用多核处理器的优势。
- 避免共享状态:在
mapToDouble
操作中,每个对象的计算都是独立的,避免了共享可变状态。
- 合适的数据量:示例中填充了大量数据,适合并行处理。实际应用中,根据具体数据量和计算复杂度调整。