2015-11-13 65 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

簡單..有一種競爭條件。將相對路徑變量的範圍更改爲局部變量。