10

在遇到 mkdirs() 的问题并浏览互联网后,我的印象是 mkdirs() 存在线程安全问题。

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

谢谢

(就我而言,我将在 Android 上使用它)

4

5 回答 5

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();
    }
}

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

于 2011-04-28T15:19:58.707 回答
3

在序列化所有内容的工作线程中创建所有目录。您可以使用 aLooper和 aHandler轻松Runnables将该调用 mkdirs 发布到您的工作线程。完成创建目录后,您可以调用 Looper.quit() 以在线程处理完最后一个 posted 后结束线程Runnable。的文档Looper有示例代码,显示了这是多么微不足道。

于 2011-03-04T04:44:14.493 回答
2

一种可能的解决方案是 MkDirService(如下图所示),它只保证一个实例并在它自己的线程中运行。使用阻塞队列。

首先是服务:

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();

    }
}
于 2011-03-04T04:18:32.843 回答
1

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

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

但是,这似乎效率低下并且仍然存在问题。那么,为什么不简单地这样做:

while (!f.mkdirs()) {}

很简单,但它有效。

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

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

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

于 2011-08-01T23:33:20.960 回答
1

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

package service;

import java.io.File;

public class FileService {

    public static synchronized boolean mkdirs( File dir ) {
        return dir.mkdirs();
    }
}
于 2013-09-22T15:34:15.587 回答