Java 8流和并发写入

时间:2015-08-18 08:52:10

标签: java arraylist stream java-stream

我有这样的代码

    public static void main(String[] args) throws Exception {
      long start = System.currentTimeMillis();
      List<String> matches = new Vector<>(); // Race condition for ArrayList??
      BufferedReader reader = new BufferedReader(new InputStreamReader(new FileInputStream("AHugeFile.txt")));
      BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(new FileOutputStream("output.txt")));
      reader.lines().parallel()
         .filter(s -> s.matches("someFancyRegEx"))
         .forEach(s -> {
               matches.add(s);
               try {
                  writer.write(s);
                  writer.newLine();
               } catch (Exception e) {
                  System.out.println("error");
               }
            }
         );
      out.println("Processing took " + (System.currentTimeMillis() - start) / 1000 + " seconds and matches " + matches.size());
      reader.close();
      writer.flush();
      writer.close();
   }

我注意到如果我用第3行的ArrayList替换Vector,我每次都会在匹配中得到不同的结果。我只是在Streams上弄脏手,但是假设forEach同时执行尝试写入ArrayList而错过了一些写操作!使用Vector,结果是一致的。

我有两个问题:

  1. 我对ArrayList的推理导致RACE正确吗?
  2. 鉴于&#39;写&#39;也是在同一个终端操作中写入文件,可以写'&#39;可能会错过一些线?在我的测试中,运行程序几次,结果似乎与写出的正确行数一致。

2 个答案:

答案 0 :(得分:2)

首先要做的事情:定义你是否关心线的编写顺序; .forEach() strips the ORDERED characteristic of a Spliterator (been there, done that)

第二:使用Java 8提供的工具;它有两个非常方便的方法,Files.lines()Files.write()

第三:正确处理你的资源!您的代码无法保证文件描述符将被正确关闭。

第四:.matches()每次都会重新创建一个Pattern并且你总是使用相同的正则表达式进行过滤......你在浪费资源。

第五:鉴于BufferedWriter的写方法是同步的,你并没有通过并行获得太多收益。

我将如何做到这一点:

public static void writeFiltered(final Path srcFile, final Path dstFile,
    final String regex)
    throws IOException
{
    final Pattern pattern = Pattern.compile(regex);

    final List<String> filteredLines;

    try (
        // UTF-8 by default
        final Stream<String> srcLines = Files.lines(srcFile);
    ) {
        filteredLines = srcLines.map(pattern::matcher)
            .filter(Matcher::matches)
            .collect(Collectors.toList());
    }

    // UTF-8 by default
    Files.write(dstFile, filteredLines);
}

答案 1 :(得分:0)

  1. ArrayList不是同步集合,所以是的,它会导致RACE条件。所有改变向量状态的方法都是同步的,因此你没有发现任何问题。

  2. BufferedWriter的write方法是同步的,因此所有写入在线程中都是一致的。因此,文件中的单个写操作将是线程安全的。但是您需要显式处理同步以使其在线程中保持一致。

  3. 以下是Java 6中write方法的代码片段。

    public void write(String s, int off, int len) throws IOException {
    
        synchronized (lock) {
    
            ensureOpen();
    
            int b = off, t = off + len;
    
            while (b < t) {
    
                int d = min(nChars - nextChar, t - b);
                s.getChars(b, b + d, cb, nextChar);
                b += d;
                nextChar += d;
    
                if (nextChar >= nChars)
                    flushBuffer();
                }
            }
        }
    }