2017-09-24 87 views
0

以下是我将类型为ConcurrentMap<String, List<String>>写入文件的类。地图中的关键字是路径,地图中的值将按顺序写入文件。这Task<Void>被调用每一次有1000个值地图:Java多重打开和关闭写入文件

public class MapWriter extends Task<Void> { 

private final ParsingProducerConsumerContext context; 

public MapWriter(ParsingProducerConsumerContext context) { 
    this.context = context; 
} 

@Override 
protected Void call() throws Exception { 
    if (!isCancelled() || !context.isEmpty()) { 
     ConcurrentMap<String, List<String>> jsonObjectMap = context.fetchAndReset(); 

     jsonObjectMap.entrySet().forEach((t) -> {     
      try { 
       FileUtils.writeLines(new File(context.getPath() + t.getKey() + "\\sorted.json"), t.getValue(), true); 
      } catch (IOException ex) { 
       context.getLogger().log("Error writing to disk:"); 
       context.getLogger().log(ex.toString()); 
       context.stopEverything(); 
      } 
     }); 

     context.getLogger().log(jsonObjectMap.values().stream().mapToInt(List::size).sum() + " schedules written to disk "); 
    } else { 
     context.getLogger().log("Nothing to write"); 
    } 

    return null; 
} 
} 

所有这些任务运行的同时,有一个制片人Task逐行读取〜2GByte文件中的行,它获取由消费者和处理放入ConcurrentMap<String, List<String>>

虽然这可行,但它非常缓慢!

我的研究表明,重复打开和关闭文件会产生足够的开销,从而影响性能,所以想知道以下方法是否会更好?

维护一个Map<String, File>File对象是开放的。 如果ConcurrentMap<String, List<String>>中的键对应于一个打开的文件,请使用该File参考编写 当所有处理完成后,循环使用Map<String, File>值并关闭每个文件。

这听起来是一个明智的方式吗?将会有大约100个文件被打开。

编辑::我做了一个简单的基准使用System.nanoTime()。生产者逐行导入的文件约为2GB,每行在6kb到10kb之间(在List<String>中)。

此外,遇到OutOfMemory错误!我猜是因为2GByte被有效地加载到内存中,而不是写得很快?

514 jsonObjects written to disk in 2258007ms 538 jsonObjects written to disk in 2525166ms 1372 jsonObjects written to disk in 169959ms 1690 jsonObjects written to disk in 720824ms 9079 jsonObjects written to disk in 5221168ms 22552 jsonObjects written to disk in 6943207ms 13392 jsonObjects written to disk in 6475639ms 0 jsonObjects written to disk in 6ms 0 jsonObjects written to disk in 5ms 0 jsonObjects written to disk in 5ms 40 jsonObjects written to disk in 23108ms 631 jsonObjects written to disk in 200269ms 3883 jsonObjects written to disk in 2054177ms Producer failed with java.lang.OutOfMemoryError: GC overhead limit exceeded

为了完整起见,这里是制作类:

public class NRODJsonProducer extends Task<Void> { 

private final ParsingProducerConsumerContext context; 

public NRODJsonProducer(ParsingProducerConsumerContext context) { 
    this.context = context; 
} 

@Override 
protected Void call() throws Exception { 
    context.getLogger().log("Producer created"); 

    LineIterator li = FileUtils.lineIterator(new File(context.getPath() + context.getFilterFile())); 

    while (li.hasNext()) { 
     try { 
      context.getQueue().put(li.next()); 
     } catch (InterruptedException ex) { 
      Logger.getLogger(NRODJsonProducer.class.getName()).log(Level.SEVERE, null, ex); 
     } 
    } 

    LineIterator.closeQuietly(li); 

    context.getLogger().log("Producer finished..."); 

    return null; 
} 

}

+0

给它一个去吧,让我们知道吗? –

回答

0

我不明白为什么。此代码将一个密钥的所有内容写出到同名文件中,然后转到下一个密钥。如果生产者为该密钥生成另一个条目,它将覆盖前一个条目,并且此代码将再次写入该文件。保持文件打开不会有帮助。

真正的问题似乎是你一直在向文件写入相同的数据,因为你永远不会从地图中删除处理过的密钥。

注意您的使用条件有误。它应该是

if (!isCancelled() && !context.isEmpty()) 
+0

啊......当调用'context.fetchAndReset()'时,它会从'context'中检索映射。上下文中的地图被替换为新的地图,所以'MapWriter'一直都有它,它只会被读取然后被丢弃。 – swshaun