java - 创建符号链接(symbolic link)时的并发执行异常和 nio.file.NoSuchFileException

标签 java multithreading nio

我有一个包含 JSON 文件的目录,需要迭代该目录才能获取文档 dName 的名称。多个 JSON 文件可以具有相同的 dName。然后,需要从名为 output/dName/match 的文件夹创建到该 JSON 文件的符号链接(symbolic link)。线程首先检查 dName 文件夹是否存在,如果不存在,则首先创建它们。我有以下创建符号链接(symbolic link)的代码。

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

但是,在创建符号链接(symbolic link)的行处不断发生异常。

Exception in thread "main" java.lang.Exception: java.util.concurrent.ExecutionException: java.lang.RuntimeException: java.nio.file.NoSuchFileException:`

例如:output/document1/match/ok.json处出现异常

如果我是对的,只有在执行该行之后才会创建符号链接(symbolic link)。那么为什么会出现这个错误呢?线程创建的单个符号链接(symbolic link)为什么会导致concurrent.ExecutionException?

最佳答案

Then why does the error occur?

发生错误的原因是您的“父目录创建”在创建符号链接(symbolic link)之前未创建所有父目录。例如:如果您有 "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 而不是 a/b/c 目录。经典的并发问题。将该 path 设为局部变量将立即解决您的问题。或者在单线程中运行您的进程。

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

And individual Symbolic Link creation by a thread why does it cause concurrent.ExecutionException?

当您对 future 执行 get 时,NoSuchFileException 会被您的 RunTimeException 包装,而 RunTimeException 会被 ExecutionException 包装。因为, RunTimeException 发生在不同的线程上,而您的以下调用发生在主线程上。因此,Executor 包装了 Exception 并在以下从主线程调用的调用中触发。

future.get();

谢谢。

关于java - 创建符号链接(symbolic link)时的并发执行异常和 nio.file.NoSuchFileException,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/33697054/

相关文章:

c - 通过线程进行多次访问导致数据丢失的搜索示例(C 语言)

multithreading - 在Julia 1.5.2中获取锁时出错

java - 连接到 Mpos 设备抛出 java.nio.channels.IllegalBlockingModeException

java - 使用 Java 7 仅访问特定深度的目录

java - Paths.get 找不到文件 Windows 10 (java)

java - 实现迭代器时索引不递增

java - Picasso 中的错误 get() 无法应用于 (android.content.Context)

java - 我正在尝试增加 ConcurrentHashMap 的值

由于填充,Java 通过字符串加密/解密不起作用

java - 什么是NullPointerException,我该如何解决?