创建符号链接时出现ConcurrentExecution Exception和nio.file.NoSuchFileException

问题描述 投票:1回答:4

我有一个带有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();
    }
}

但是,在创建符号链接的行中会发生异常。

线程“main”中的异常java.lang.Exception:java.util.concurrent.ExecutionException:java.lang.RuntimeException:java.nio.file.NoSuchFileException:`

例如:output/document1/match/ok.json的例外情况

如果我是对的,只有在执行该行后才会创建符号链接。那为什么会出现错误?并且由线程创建的个别符号链接为什么它会导致concurrent.ExecutionException

java multithreading nio
4个回答
1
投票

正如我的评论所述:

 path = Paths.get(opDir); 

是一种竞争条件。


2
投票

那为什么会出现错误?

发生此错误的原因是您的"parent directory creation"在创建符号链接之前未创建所有父目录。例如:如果你有"dname": "a/b/c/somedname1.txt"的json条目 - 文件夹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。或者当您没有父文件夹时尝试创建文件/符号链接时。

并且由线程创建的单个符号链接为什么会导致并发.ExecutionException?

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

future.get();

谢谢。


0
投票

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


-3
投票

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

© www.soinside.com 2019 - 2024. All rights reserved.