2011-03-04 143 views
7

在遇到与mkdirs()有关的问题并在互联网上徘徊后,我得到mkdirs()存在线程安全问题的印象。如何在Java中以线程安全的方式使用mkdirs?

有可能确保在多个线程可能尝试创建类似的文件结构时可以正确创建目录吗?

感谢

(在我的情况下,我会在Android上使用本)

+0

你能不能提供,为什么你想引用'mkdirs()'是不是线程安全的? – 2011-03-04 08:33:29

+0

是否有任何细节已知(适用于J2SE和Android)在哪种情况下,此问题很可能出现?CPU内核,线程,操作系统,文件系统的数量?我试图为测试目的提出这个问题。 – Robert 2011-07-04 09:14:05

+0

@TedHopp链接已死?你能重新链接或解释它是什么吗? – 2014-11-07 10:06:30

回答

0

好吧,我知道这已经停用了一段时间,但我想也许有一个简单的解决方案。您在该问题的评论中链接的文章似乎表明,唯一的问题是目录而不是正在创建。解决方案是这样做的:

if (!f.mkdirs()) { 
    f.mkdirs(); 
} 

但是,这似乎效率低下,仍然可能有问题。那么,为什么不简单地这样做:

while (!f.mkdirs()) {} 

很简单,但它的工作原理。

编辑:思考了一下之后,这个例子可能滞后于遗忘,并可能导致线程锁定。所以,这可能是一个更好的主意:

while (!f.mkdirs()) { Thread.yield(); } 

当然,如果你在这可能导致螺纹锁,只要它不是一个高优先级的情况下一个线程是那个只会建议。把它放在那里。

+0

...真的,真的希望这种情况不会在mkdirs()因为真正的原因而失败的情况下运行,例如权限问题或格式错误的目录名称。 – 2012-10-01 15:17:18

+0

@smackfu好点。也许最好尝试一定次数,然后显示错误消息。 – 2012-10-01 20:19:39

+0

我有确切的问题,遗憾的是没有上述对我的工作,但这个工作',而(!dir.mkdirs()) \t \t {\t \t \t \t如果(dir.isDirectory()) \t \t \t { \t \t \t \t break; \t \t \t} \t \t \t \t \t}' – 2014-11-07 12:03:08

2

一个可能的解决方案将是一个MkDirService(如下图所示),保证只有一个实例,并在它自己的线程中运行。利用BlockingQueue。

首先服务:

package mkdir; 

import java.io.File; 
import java.util.concurrent.BlockingQueue; 
import java.util.concurrent.LinkedBlockingQueue; 

public class MkDirService extends Thread { 

    private static MkDirService service; 
    private BlockingQueue<File> pendingDirs = new LinkedBlockingQueue<File>(); 
    private boolean run = true; 

    private MkDirService() { 
    } 

    public synchronized static MkDirService getService() { 
     if (service == null) { 
      service = new MkDirService(); 
      new Thread(service).start(); 
     } 
     return service; 
    } 

    public void makeDir(File dir) { 
     pendingDirs.add(dir); 
    } 

    public void shutdown() { 
     run = false; 
    } 

    @Override 
    public void run() { 
     while (run || !pendingDirs.isEmpty()) { 
      File curDir = null; 
      try { 
       curDir = pendingDirs.take(); 
      } catch (InterruptedException e) { 
       e.printStackTrace(); 
      } 
      if (curDir != null && !curDir.exists()) { 
       curDir.mkdir(); 
       System.out.println("Made: " + curDir.getAbsolutePath()); 
      } 
     } 
    } 
} 

的测试:

package mkdir; 

import java.io.File; 

public class MkDirServiceTest { 

    /** 
    * @param args 
    */ 
    public static void main(String[] args) { 
     MkDirService mdServ = MkDirService.getService(); 
     mdServ.makeDir(new File("test1")); 
     mdServ.makeDir(new File("test1/test2")); 
     mdServ.makeDir(new File("test1/test3")); 
     mdServ.shutdown(); 

    } 
} 
+1

我会欢迎任何对我的例子的更正,我相信有一些更好的方法来做到这一点,并会认为这是一个很好的学习机会。 – bconneen 2011-03-04 05:04:59

3

不要在序列化的一切工作线程所有目录的创建。您可以使用LooperHandler来简化将调用mkdirs的Runnables发布到工作线程。完成目录制作后,可以调用Looper.quit()在处理最后发布的Runnable之后结束该线程。该documentation for Looper有示例代码,显示这是多么接近微不足道。

5

我不知道,如果Android支持的并发包,但这里是我的看法:

private static Lock fsLock = new ReentrantLock(); 

private void mkdir(File dir) throws FileNotFoundException { 

    if(dir.exists()) { 
     return; 
    } 

    fsLock.lock(); 
    try { 
     if(!dir.exists()) { 
      log.info("Creating directory {}", dir.getAbsolutePath()); 
      if(!dir.mkdirs()) { 
       throw new FileNotFoundException("Can't create directory " + dir.getAbsolutePath()); 
      } 
     } 
    } finally { 
     fsLock.unlock(); 
    } 
} 

的方法早期如果该目录已经存在返回。如果它不存在,只有一个线程会尝试创建它。

+0

这太好了。这解决了我与多线程尝试创建相同目录的问题。不是在Android上,而是在常规的Java 5应用程序中。 – Melloware 2011-07-19 15:54:14

+0

是的,这比看起来更难。 – 2011-07-19 16:28:54

1

Eaven如果该线程是一个年纪大一点我不知道是否有somethink错了以下解决方案:

package service; 

import java.io.File; 

public class FileService { 

    public static synchronized boolean mkdirs(File dir) { 
     return dir.mkdirs(); 
    } 
} 
相关问题