我正在尝试提高应用程序中I / O的速度,所以我决定使用多个线程来存储它。文件在层次结构书/符号/文件中构成,多个线程可能同时在同一目录中保存多个文件。当我按顺序保存所有文件时,没有问题。但是,当多个线程启动时,有时文件格式错误并且加载它会引发“IOException:invalid block”。在这种情况下,为什么并发可能会搞砸?
以下代码:
private void storeAppendingTimestamps(Series timeSeries) throws MetricPersistException {
Metric metric = timeSeries.getMetric();
Path outPutFile;
try {
outPutFile = generateOutputFilePath(metric);
if (!Files.exists(outPutFile)) {
createNewFile(outPutFile);
}
} catch (IOException e) {
throw new PersistException("Cannot create output file for metric " + metric);
}
try (PrintWriter writer = new PrintWriter(new GZIPOutputStream(new FileOutputStream(outPutFile.toFile(), true)), true)) {
for (SeriesDataPoint dataPoint : timeSeries.getTimeSeriesPoints()) {
writer.println(String.format("%d %s", dataPoint.getTimestamp().getMillis(), formatPlain(dataPoint.getValue())));
}
writer.close();
} catch (IOException e) {
throw new MetricPersistException(String.format("IO Exception has occured while persisting metric %s: %s", metric, e.getMessage()));
}
}
分割工作的代码:
private void persistTimeSeries(Collection<Series> allSeries, CompletionService<Void> executorService) throws MetricPersistException {
final LoggingCounter counter = new LoggingCounter(logger, "metric series file", 10000);
for (final MetricTimeSeries series : allSeries) {
executorService.submit(new Callable<Void>() {
@Override
public Void call() throws Exception {
persister.persistTimeSeries(series);
counter.increment();
return null;
}
});
}
for (int i = 0; i < allSeries.size(); i++) {
Future<Void> future = executorService.take();
future.get();
}
counter.finish();
}
答案 0 :(得分:1)
如果您正在寻找性能,那么通过在BufferedOutputStream
和GZIPOutputStram
之间引入FileOutputStream
,您几乎肯定会获得更大的性能提升。
虽然您正在使用它,但添加一个具有正确编码规范的OutputStreamWriter
,以便在您的特定计算机上运行的人能够正确解释该文件。< / p>
我看到的一个明显线程不安全的代码是storeAppendingTimestamps()
:如果你有几个系列会映射到同一个输出文件,那么你就有竞争条件,它们都打开并写入相同的文件(创建文件时也存在竞争条件,但我认为这是一种幂等操作)。
如果你可能有多个系列映射到相同的文件名,那么你需要一个线程安全/种族安全的防护。像ConcurrentHashMap
这样的东西,用于存储您正在处理的文件的名称。如果发生碰撞,请退出任务(带警告)。