我编写了这个程序来测试mkdir()
失败的场景。为什么会失败?
有时它工作正常,有时我得到:
无法创建DIR :: myDir4 无法创建DIR :: myDir4
在最后我发现每个目录都已创建......
在每次测试中,我都会删除所有创建的目录。
我试过这个,因为在我的项目中有100个线程试图测试和创建这样的目录......并且也以同样的方式失败......
public class DFS {
static long time1 = System.currentTimeMillis();
public static void main(String a[]) {
new Thread(new CreteDir()).start();
new Thread(new CreteDir()).start();
new Thread(new CreteDir()).start();
new Thread(new CreteDir()).start();
new Thread(new CreteDir()).start();
new Thread(new CreteDir()).start();
new Thread(new CreteDir()).start();
new Thread(new CreteDir()).start();
}
}
class CreteDir implements Runnable {
public void run() {
//Object obj = new Object();
synchronized (this) {
if(System.currentTimeMillis() - DFS.time1 > 10) {
try {
this.wait();
}
catch(InterruptedException ie) {
ie.printStackTrace();
}
}
File f1 = new File("myDir1");
File f2 = new File("myDir2");
File f3 = new File("myDir3");
File f4 = new File("myDir4");
File f5 = new File("myDir5");
if (!f1.exists()&&!f1.mkdir()) {
System.out.println("Cannot create DIR :: "+f1.getName());
}
if (!f2.exists()&&!f2.mkdir()) {
System.out.println("Cannot create DIR :: "+f2.getName());
}
if (!f3.exists()&&!f3.mkdir()) {
System.out.println("Cannot create DIR :: "+f3.getName());
}
if (!f4.exists()&&!f4.mkdir()) {
System.out.println("Cannot create DIR :: "+f4.getName());
}
if (!f5.exists()&&!f5.mkdir()) {
System.out.println("Cannot create DIR :: "+f5.getName());
}
this.notifyAll();
}
}
}
答案 0 :(得分:11)
你有竞争条件。
每个线程都会尝试检查每个目录,如果它还不存在则创建它。发生的事情是这样的:
A
测试myDir4
并发现它不存在B
测试myDir4
并发现它不存在A
创建myDir4
...成功!B
创建myDir4
...失败!它已经存在。任何目录都可能发生这种情况......或根本不存在......具体取决于操作系统如何安排Java线程,等等。
您的代码正在尝试在this
上进行同步,但尝试无效。 this
将是当前线程正在使用的CreteDir
的实例...但每个线程将具有不同的实例,因此实际上没有线程间同步。为了有效地进行同步,所有线程都需要在同一个对象上进行同步...但这会使您的多线程无效,因为粒度是错误的。
事实上,您的整个多线程策略需要重新考虑。事实上,这不是“真正的代码”意味着我们无法真正建议你如何做到这一点。
在您上次评论的各行之间阅读,我认为您有三种可能的策略:
只需使用“全局”锁定即可在不存在的情况下同步目录的创建。像这样:
// Test first without locking to reduce the concurrency bottleneck
if (!dir.exists()) {
synchronize (globalDirLock) {
// Repeat the test while holding the lock
if (!dir.exists()) {
if (!dir.mkdir()) {
System.out.println("OOOPS!");
}
}
}
}
创建一个内存数据结构,每个目录有一个(锁定)对象。需要小心地填充该数据结构以避免竞争条件,其中两个同时的客户端请求最终为单个目录创建两个锁定对象。
(如果你稍微调整一下这个方案,你也可以使用锁定对象的存在来避免重复检查目录是否存在。检查涉及系统调用,并且在处理器开销方面是非常重要的。)< / p>
只需忽略File.mkdir()
返回false
的情况。 (也许可以进行另一次File.exists()
或File.isDirectory()
测试...以防万一。)
答案 1 :(得分:0)
您可以尝试以下操作:
,而不是添加全局锁定if (!f1.exists()&&!f1.mkdir()&&!f1.isDirectory()) {
System.out.println("Cannot create DIR :: "+f1.getName());
}
isDirectory()
将告知此目录是否已创建。