2015-11-13 63 views
1

我有一个Dir与json文件需要迭代获取文件名称dName。多个json文件可以有相同的dName然后需要从名为output/dName/match的文件夹创建该json文件的符号链接。线程首先检查dName文件夹是否先存在,如果不存在,则首先创建它们。我有以下代码创建符号链接。在output/document1/match/ok.json异常,如果我是正确的这一行被执行后,才创建符号链接:ConcurrentExecution Exception&nio.file.NoSuchFileException当创建符号链接

protected static void copyFile(String docName, Path tFilePath) throws IOException { 
    final String docFolderName = "output" + docName.substring(0, docName.lastIndexOf(".")); 
    final String opDir = docFolderName + "match"; 
    path = Paths.get(opDir); 
    if (Files.notExists(path)) { 
     Files.createDirectories(path); 
     outputAnnotationDirs.add(path.toString()); 
    } 
    try { 
     Files.createSymbolicLink(Paths.get(opDir).resolve(tFilePath.getFileName()), tFilePath); 
    } catch (IOException e) { 
     throw new RuntimeException(e); 
    } 
} 


protected static void Mapper(String Dir,int numThreads) throws Exception { 
    final ExecutorService executorService = Executors.newFixedThreadPool(numThreads); 
    final ConcurrentLinkedQueue<Future<?>> futures = new ConcurrentLinkedQueue<Future<?>>(); 
    final JsonParser parser = new JsonParser(); 
    try { 
     Files.walkFileTree(Paths.get(Dir), new SimpleFileVisitor<Path>() { 
      @Override 
      public FileVisitResult visitFile(final Path tFile, BasicFileAttributes attrs) throws IOException { 
       futures.add((Future<String>) executorService.submit(new Runnable() { 
        public void run() { 
         JsonObject jsonObject = null; 
         FileReader reader = null; 
         try { 
          reader = new FileReader(tFile.toFile()); 
          jsonObject = (JsonObject) parser.parse(reader); 
          JsonArray instancesArray = (JsonArray) jsonObject.get("instances"); 
          String dName = instancesArray.get(0).getAsJsonObject().get("dname").toString(); 
          copyFile(dName, tFile); 
         } catch (FileNotFoundException e) { 
          throw new RuntimeException(e); 
         } catch (Exception e) { 
          throw new RuntimeException(e); 
         } finally { 
          try { 
           if (reader != null) 
            reader.close(); 
          } catch (IOException e) { 

           logger.error(e); 
          } 
         }       
        } 
       })); 
       return FileVisitResult.CONTINUE; 
      } 

     }); 
    } catch (Exception e) { 
     throw new RuntimeException(e); 
    } finally { 
     Future<?> future; 
     while ((future = futures.poll()) != null) { 
      try { 
       future.get(); 
      } catch (Exception e) { 
       for (Future<?> f : futures) 
        f.cancel(true); 
       if (executorService != null) 
        executorService.shutdown(); 
       throw new Exception(e); 
      } 
     } 
     if (executorService != null) 
      executorService.shutdown(); 
    } 
} 

然而Exception in thread "main" java.lang.Exception: java.util.concurrent.ExecutionException: java.lang.RuntimeException: java.nio.file.NoSuchFileException:不断出现@其中符号链接created.Ex行。那为什么会发生错误?由线程创建单个符号链接为什么它会导致并发.ExecutionException?

+0

path = Paths.get(opDir);可能是一种竞争条件。你可以尝试使用局部变量吗?你能粘贴完整的Stacktrace吗? –

+0

这是问题..请发布相同的答案,以便我可以奖励你回答赏金:) – prem89

回答

1

正如我在评论中指出:

path = Paths.get(opDir); 

是一个竞争条件。

-2

对我来说,它看起来像NoSuchFileException告诉你什么是问题。要创建文件的符号链接,该文件应该存在。

+0

创建符号链接的json文件确实存在!它说符号链接文件不存在,这是奇怪的,因为该声明应该创建它.. – prem89

+0

这是错误的。符号链接的目标不需要存在。 – Kenster

1

那么为什么会出现错误呢?

发生错误是因为您的"parent directory creation"在创建符号链接之前未创建所有父目录。例如:如果您有json条目"dname": "a/b/c/somedname1.txt" - 文件夹a/b/c似乎并未创建。这就是为什么NoSuchFileException被抛出。现在,你已经有了创建目录的逻辑,但为什么这不起作用呢?如果你在一个线程中运行它,那将会工作得很好。为什么不在多个线程中?

因为path变量在所有线程之间共享,并且同时被许多线程修改。

path = Paths.get(opDir); 
if (Files.notExists(path)) { 
    Files.createDirectories(path); 
    outputAnnotationDirs.add(path.toString()); 
} 

当多个线程运行,比方说,一个线程dname:a/b/c/dname1.txt和第二线程具有dname:e/f/g/dname2.txt。第一个线程最终可能会创建e/f/g而不是/ b/c目录。经典的并发问题。使path为局部变量将立即解决您的问题。或者在一个线程中运行你的进程。

  1. 如果您的原始文件被另一个进程删除,您将得到一个java.io.FileNotFoundException
  2. 如果您的符号链接已经存在,您将得到一个java.nio.file.FileAlreadyExistsException
  3. java.nio.file.NoSuchFileException发生在无法对文件执行操作时,如DELETE。或者,当您尝试在父文件夹不存在时创建文件/符号链接时。

而且由一个线程个体符号链接创建为什么它会导致 concurrent.ExecutionException?

NoSuchFileException由您RunTimeException包裹由ExecutionException当你在futureget包裹。因为RunTimeException发生在另一个线程上,并且下面的调用发生在主线程上。所以Executor包装了Exception并且触发了从主线程调用的下面的调用。

future.get(); 

谢谢。

1

简单..有一种竞争条件。将相对路径变量的范围更改为局部变量。