6

我正在创建文件和文件夹树。我正在重写多线程。我看到的唯一弱点是创建文件夹时。现在它一一进行(深入)。在我写下文件之前,我检查路径是否存在。如果没有,我会使用 mkdirs 来创建所有缺失的内容。

public void checkDir(String relativePath) {
        File file = new File(homePath + relativePath);
        if (!file.exists()) {
            if (file.mkdirs()) {
                log.info("Directory: " + homePath + relativePath + " is created!");
            } else {
                log.error("Failed to create directory: " + homePath + relativePath + " !");
            }
        }
    }

我有一个问题,当我使用两个线程时会发生什么。一个有路径 A/B/C,另一个有 A/B/D。假设我只有 A 文件夹,但没有 B。所以他们俩都会检查该路径是否不存在并想要创建它。所以其中一个可能会失败,因为另一个会更快。那么我该如何管理呢?

  1. 我正在考虑删除存在条件并让它失败,但是我没有可以捕获的 AlreadyExists 异常..
  2. 首先创建目录树(但我认为会有更好的方法?)
  3. 将目录创建作为关键部分并使其按顺序进行 - 不确定如何在春季执行此操作,但无论如何热确保它是必要的并且不会减慢进程太多。

也许是我想太多了,但理论上这种情况可能会发生。目前我使用常规线程,但我想为此使用 spring TaskExecutor。它自己处理关键部分,但这不是共享变量或任何东西,而且路径不同,所以我认为它不会识别它。

感谢您的建议。

4

3 回答 3

7

指定该File.mkdirs()方法来创建目录及其所有父目录(如果它们不存在)。因此,无论如何调用Existence是没有意义的exists().。打电话exists()只是浪费时间。mkdirs()本质上是一个原子操作:试图超越它真的没有意义。

请注意,返回值false不一定是失败。它可能只是表明路径中的所有目录都已经存在。

基本上你的问题的前提是错误的。

于 2015-04-07T09:54:34.517 回答
5

没有一个答案似乎解决了 mkdirs() 是否是线程安全的问题,一个答案指出 mkdirs() 是原子的,但可能存在失败的情况。此函数本质上处理文件系统,因此它可能涉及对相应主机上的操作系统的系统调用,并且如果您还不知道应用程序将要使用的目标系统,那么确定这些系统调用是否实际上是线程安全的可能是不可能的上使用。

例如,即使 mkdirs() 在创建文件夹结构之前检查是否存在,在以下情况下会发生什么,

线程 1 调用 mkdirs(),它固有地检查文件夹结构的存在并确定它不存在。那时,线程 1 被抢占。

线程 2 调用 mkdirs(),它固有地检查文件夹结构的存在并确定它不存在,然后继续创建文件夹结构。

线程 1 再次启动并继续尝试创建文件夹结构,之前确定它以前不存在。

那里会发生什么?我不知道,这一系列事件很难测试,尤其是在知道创建文件夹系统调用因操作系统而异的情况下。线程安全和避免引入可能难以跟踪和调试的错误的最佳选择是在代码的这个关键部分实现一定程度的互斥。

我想采取一种天真的方法并声明一个两个线程都可以访问的单个“全局”变量会很容易,例如布尔值 b 然后在您的关键部分周围添加以下代码,

synchronized(b) {
     // Your critical section here
}

这将保证如果一个线程已锁定 b,则它只能在另一个线程等待时访问临界区,从而确保 mkdir() 不会被两个线程调用。

但是,如果您想了解有关多线程以及如何在较低级别上实现互斥的更多信息,在这种情况下,我建议您查看信号量以及如何实现它们来解决这个问题。

于 2015-04-08T10:09:55.853 回答
1

正如 EJP 指出的那样,返回false可能意味着很多事情,有些错误,有些则不然。如果你想记录它实际上无法创建目录的事实,你应该在之后检查是否存在:

public final class DirectoryHelper {
   private DirectoryHelper(){}

   public static boolean createDirectories(File path) {
      if (path.mkdirs()) return true; //definitely has new dir
      // if false, just look afterwards for the existence of the directory
      // also opportunity to throw own exceptions if you prefer
      return path.exists() && path.isDirectory();
   }
}

我在这里写了一个新方法,只有在之后目录不存在时才返回 false。我不在乎它是刚刚制造出来还是已经存在。由于新订单,我也不需要synchronized阻止。

您的代码如下所示:

public void checkDir(String relativePath) {
    File file = new File(new File(homePath), relativePath);
    if (!file.exists()) { // just to prevent logging of existing dirs
        if (DirectoryHelper.createDirectories(file)) {
            log.info("Directory: " + file + " is created!");
        } else {
            log.error("Failed to create directory: " + file + " !");
        }
    }
};
于 2015-04-07T10:47:41.233 回答