簡體   English   中英

創建符號鏈接時,ConcurrentExecution異常和nio.file.NoSuchFileException

[英]ConcurrentExecution Exception & nio.file.NoSuchFileException when creating Symbolic Link

我有一個包含JSON文件的目錄,需要對其進行迭代以獲取文檔dName的名稱。 多個JSON文件可以具有相同的dName 然后,需要從名為output/dName/match的文件夾中創建到該JSON文件的符號鏈接。 線程首先檢查dName文件夾是否首先存在,否則首先創建它們。 我有以下代碼創建符號鏈接。

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();
    }
}

但是,在創建符號鏈接的行上總是發生異常。

線程“主”中的異常java.lang.Exception:java.util.concurrent.ExecutionException:java.lang.RuntimeException:java.nio.file.NoSuchFileException:`

例如: output/document1/match/ok.json

如果我是對的,則僅在執行該行之后創建符號鏈接。 那為什么會發生錯誤呢? 並且由線程創建單獨的符號鏈接,為什么會導致concurrent.ExecutionException

那為什么會發生錯誤呢?

發生錯誤是因為"parent directory creation"符號鏈接”之前,您的"parent directory creation"未創建所有父目錄。 例如:如果您有"dname": "a/b/c/somedname1.txt" 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而不是a / b / c目錄。 經典的並發問題。 path為局部變量將立即解決您的問題。 或在單個線程中運行您的進程。

  1. 如果您的原始文件被另一個進程刪除,則將獲得java.io.FileNotFoundException
  2. 如果您的符號鏈接已經存在,那么您將獲得java.nio.file.FileAlreadyExistsException
  3. 當您無法對文件執行操作(例如DELETE)時,就會發生java.nio.file.NoSuchFileException 或者,當您沒有父文件夾時嘗試創建文件/符號鏈接時。

並且由線程創建單獨的符號鏈接,為什么會導致並發.ExecutionException?

NoSuchFileException你被包裹RunTimeException被包裹ExecutionException當你做一個getfuture 因為,RunTimeException發生在另一個線程上,而您的以下調用發生在主線程上。 因此,Executor包裝了Exception並在下面的調用中觸發,該調用是從主線程調用的。

future.get();

謝謝。

如我的評論所述:

 path = Paths.get(opDir); 

是比賽條件。

很簡單。有一個競賽條件。 將相對路徑變量的范圍更改為局部變量。

在我看來,NoSuchFileException告訴您問題出在哪里。 要創建指向文件的符號鏈接,該文件應存在。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM