如何在Java中以线程安全的方式使用mkdirs?

时间:2011-03-04 04:07:49

标签: java android io thread-safety mkdir

在遇到mkdirs()问题并且在互联网上闲逛之后,我觉得mkdirs()存在线程安全问题。

当多个线程可能尝试创建类似的文件结构时,有没有办法确保正确创建目录?

由于

(在我的情况下,我将在Android上使用此功能)

5 个答案:

答案 0 :(得分: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();
    }
}

如果目录已存在,则该方法会提前返回。如果它不存在,则只有一个线程会尝试创建它。

答案 1 :(得分:3)

在一个序列化所有内容的工作线程中创建所有目录。您可以使用LooperHandler来轻松发布调用mkdirs到您的工作线程的Runnables。完成目录后,可以在处理完最后发布的Runnable后调用Looper.quit()来结束线程。 documentation for Looper有一个示例代码,显示了这样做的琐碎程度。

答案 2 :(得分: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();

    }
}

答案 3 :(得分:1)

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

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

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

while (!f.mkdirs()) {}

简单,但有效。

编辑:经过一番思考后,该示例可能会滞后于遗忘并可能导致线程锁定。所以,这可能是一个更好的主意:

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

当然,只有在你可能导致线程锁定的线程中,并且只要它不是高优先级的情况时,才会建议这样做。把它放在那里。

答案 4 :(得分:1)

即使这个线程有点老,我想知道以下解决方案是否有问题:

package service;

import java.io.File;

public class FileService {

    public static synchronized boolean mkdirs( File dir ) {
        return dir.mkdirs();
    }
}