我正在创建文件和文件夹树。我正在改写多线程。我看到的唯一弱点是创建文件夹时。现在它一个接一个(深入)。在我写下文件之前,我会检查路径是否存在。如果没有,我使用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.让我们说我只有一个文件夹但不存在B.所以他们都会检查路径是否存在并且想要创建它。所以其中一个可能会失败,因为另一个会更快。那我该怎么办呢?
也许我过分思考,但从理论上讲,这种情况可能会发生。目前我使用常规Thread,但我想使用spring TaskExecutor。它自己处理关键部分,但这不是共享变量或任何东西,路径不同,所以我认为它不会识别它。
感谢您的建议。
答案 0 :(得分:5)
指定File.mkdirs()
方法来创建目录及其所有父目录(如果它们不存在)。 Ergo 调用exists().
没有意义无论如何都会检查存在。拨打exists()
只是在浪费时间。 mkdirs()
本质上是一种原子操作:尝试超越它是没有意义的。
请注意,false
的返回值不一定是失败。它可能只是表明路径中的所有目录都已存在。
基本上你提出的问题是假的。
答案 1 :(得分:5)
没有一个答案似乎解决了mkdirs()是否是线程安全的问题,一个答案表明mkdirs()是原子的,但可能存在失败的情况。这个函数基本上处理文件系统所以它可能涉及对相应主机上的操作系统的系统调用,并且如果你还不知道目标系统,那么确定这些系统调用是否实际上是线程安全的是不可能的。应用程序将用于。
例如,即使mkdirs()在创建文件夹结构之前检查是否存在,在以下情况下会发生什么,
线程1调用mkdirs(),它本身会检查文件夹结构的存在并确定它不存在。那时,线程1被抢先一步。
线程2调用mkdirs(),它本身会检查文件夹结构是否存在,并确定它不存在,然后继续创建文件夹结构。
线程1再次启动,并尝试创建文件夹结构,之前确定它之前不存在。
那里发生了什么?我不知道,这一系列事件很难测试,尤其是在创建文件夹系统调用在操作系统之间变化的情况下。对于线程安全而言,最好的选择是避免引入可能难以跟踪和调试的错误,这将是在代码中的这个关键部分实现一定程度的互斥。
我想采取一种天真的做法并宣布单一的“全球”是很容易的。两个线程都可以访问的变量,例如布尔值b,然后在关键部分周围添加以下代码,
synchronized(b) {
// Your critical section here
}
这将保证如果一个线程已经锁定b,它将仅在另一个线程等待时才能访问临界区,从而确保mkdir()不会被两个线程调用。
但是,如果您想了解有关多线程以及如何在较低级别实现互斥的更多信息,在这种情况下,我建议您查看信号量以及如何实现信号量来解决这个问题。 / p>
答案 2 :(得分: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 + " !");
}
}
};