Java8流和并发写入

Java8流和并发写入,java,arraylist,stream,java-stream,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 Buffered

我有这样的代码

    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();
   }
publicstaticvoidmain(字符串[]args)引发异常{
长启动=System.currentTimeMillis();
List matches=new Vector();//ArrayList的竞争条件??
BufferedReader=新的BufferedReader(新的InputStreamReader(新文件InputStream(“AHugeFile.txt”));
BufferedWriter writer=新的BufferedWriter(新的OutputStreamWriter(新的FileOutputStream(“output.txt”));
reader.lines().parallel()
.filter(s->s.matches(“someFancyRegEx”))
.forEach(s->{
匹配项。添加(s);
试一试{
作者:写;
writer.newLine();
}捕获(例外e){
System.out.println(“错误”);
}
}
);
out.println(“处理耗时”+(System.currentTimeMillis()-start)/1000+“秒和匹配数”+匹配数.size());
reader.close();
writer.flush();
writer.close();
}
我注意到,如果我在第3行用ArrayList替换向量,每次在匹配中都会得到不同的结果。我只是想把我的手弄脏在流上,但假设forEach同时执行,试图写入错过了一些写入的ArrayList!对于向量,结果是一致的

我有两个问题:

  • 我关于ArrayList导致比赛的推理正确吗
  • 考虑到“写入”也在同一终端操作中写入文件,“写入”可能会漏掉一些行吗?在我的测试中,运行程序几次,结果似乎与正确的行数一致
  • ArrayList不是已同步的集合,因此它将导致竞争条件。所有改变向量状态的方法都是同步的,所以你没有发现任何问题

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

  • 下面是Java6中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();
                }
            }
        }
    }
    
    public void write(字符串s、int off、int len)引发IOException{
    已同步(锁定){
    重新打开();
    int b=关,t=关+长;
    而(b=nChars)
    flushBuffer();
    }
    }
    }
    }
    
    第一件事:定义是否关心行的写入顺序

    第二:使用Java8提供的工具;它有两种非常方便的方法,分别是
    Files.lines()
    Files.write()

    第三:正确处理你的资源!代码中不能保证文件描述符会正确关闭

    第四:
    .matches()
    将每次重新创建一个
    模式
    ,并且您总是使用相同的正则表达式进行筛选。。。你在浪费资源

    第五:假设
    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);
    }
    
    public static void writefilted(最终路径srcFile,最终路径dstFile,
    最终字符串(正则表达式)
    抛出IOException
    {
    最终模式=Pattern.compile(regex);
    最终列表过滤线;
    试一试(
    //默认情况下为UTF-8
    最终流srcLines=Files.lines(srcFile);
    ) {
    filteredLines=srcLines.map(模式::匹配器)
    .filter(Matcher::matches)
    .collect(Collectors.toList());
    }
    //默认情况下为UTF-8
    文件。写入(dstFile、filteredLines);
    }
    
    您应该使用
    Files.newBuffered{Reader,Writer}()
    因为您使用的是Java 7+。更重要的是,Java8有
    文件.lines()
    ,您应该在试用资源时打开这些文件。-“但是您需要显式地处理同步,以使其在线程之间保持一致。”!很好的观点,特别是关于正则表达式,谢谢!我接受了先前的答案,因为它回答了我提出的两个问题,只是向你投了一票。注释,文件很大(大约60 GB),我想最好不要将匹配项存储在列表中,然后再编写它们。我想在阅读和匹配时写出。