考虑以下功能:
public void execute4() {
File filePath = new File(filePathData);
File[] files = filePath.listFiles((File filePathData) -> filePathData.getName().endsWith("CDR"));
List<CDR> cdrs = new ArrayList<CDR>();
Arrays.asList(files).parallelStream().forEach(file -> readCDRP(cdrs, file));
cdrs.sort(cdrsorter);
}
读取包含CDR的文件列表并执行readCDRP(),即:
private void readCDRP(List<CDR> cdrs, File file) {
final CDR cdr = new CDR(file.getName());
try (BufferedReader bfr = new BufferedReader(new FileReader(file))) {
List<String> lines = bfr.lines().collect(Collectors.toList());
lines.parallelStream().forEach(e -> {
String[] data = e.split(",", -1);
CDREntry entry = new CDREntry(file.getName());
for (int i = 0; i < data.length; i++) {
entry.setField(i, data[i]);
}
cdr.addEntry(entry);
});
if (cdr != null) {
cdrs.add(cdr);
}
} catch (IOException e) {
e.printStackTrace();
}
}
我观察到的是偶尔而不是所有的时间,我在readCDRP函数上得到一个ArrayIndexNotBound Exception(这很尴尬,因为cdr的列表是一个ArrayList() ):
cdr.addEntry(entry);
或 在execute4()的最后一行,我在那里应用排序。
我认为问题是来自execute4的第一个parallelStream不在readCDRP()内的第二个parallelStream执行的内存中的单独空间中,并且似乎也错误地共享数据。使用&#34;似乎&#34;我无法确认,只是一个厨房。
问题是: 从JDK8的角度来看,我的代码是错误的吗? 是否有使用相同流程的解决方法,例如使用CountDownLatch? 是对ForkJoinPool的限制吗?
感谢您的回应......
修改(1): addEntry是类本身的一部分:
class CDR {
public final String fileName;
private final List<CDREntry> entries = new ArrayList<CDREntry>();
public CDR(String fileName) {
super();
this.fileName = fileName;
}
public List<CDREntry> getEntries() {
return entries;
}
public List<CDREntry> addEntry(CDREntry e) {
entries.add(e);
return entries;
}
public String getFileName() {
return this.fileName;
}
}
答案 0 :(得分:4)
从线程安全的角度来看,您的代码已被破坏。在<a target="_blank" href="http://gerster.com/docs/posamenten_neuheiten_2014_2.pdf">Jetzt PDF-Katalog herunterladen</a>
中,您向readCDR
列表添加元素,该列表是不支持并发写入的cdrs
。这就是它破裂的原因。
更好的方法是让ArrayList
返回readCDR
个对象并执行以下操作:
cdr
此外,使用并行流进行IO相关操作通常是一个坏主意,但这是另一个讨论。
答案 1 :(得分:3)
当您以函数样式开始编程时,您应该首选可以通过构造完全创建的不可变对象(或者可能使用构建器模式或某些工厂方法)。所以你的CDREntry
课程可能如下所示:
class CDREntry {
private final String[] fields;
private final String name;
public CDREntry(String name, String[] fields) {
this.name = name;
this.fields = fields;
}
// Add getters and whatever
}
您的CDR
课程可能如下所示:
class CDR {
private final String fileName;
private final List<CDREntry> entries;
public CDR(String fileName, List<CDREntry> entries) {
this.fileName = fileName;
this.entries = entries;
}
public List<CDREntry> getEntries() {
return entries;
}
public String getFileName() {
return this.fileName;
}
}
拥有这些课程变得更容易。其余的代码可以像这样重写:
public void execute4() {
File filePath = new File(filePathData);
File[] files = filePath.listFiles((File data, String name) ->
data.getName().endsWith("CDR")); // fixed this line: it had compilation error
List<CDR> cdrs = Arrays.stream(files).parallel()
.map(this::readCDRP).sorted(cdrsorter)
.collect(Collectors.toList());
}
private CDR readCDRP(File file) {
try (BufferedReader bfr = new BufferedReader(new FileReader(file))) {
// I'm not sure that collecting lines into list
// before main processing was actually necessary
return bfr.lines().parallelStream()
.map(e -> new CDREntry(file.getName(), e.split(",", -1)))
.collect(Collectors.collectingAndThen(
Collectors.toList(), list -> new CDR(file.getName(), list)));
} catch (IOException e) {
throw new UncheckedIOException(e);
}
}
一般情况下,forEach
通常不是解决任务的最简洁方法。将流集成到遗留代码中可能会有所帮助,但通常应该避免使用。
答案 2 :(得分:2)
您正在使用并行流和具有副作用的lambda (lambda更新了ArrayList'cdrs') 尝试使用收集器或还原操作。