繁体   English   中英

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

[英]How to use mkdirs in a thread safe manner in Java?

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

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

谢谢

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

我不确定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();
    }
}

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

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

一种可能的解决方案是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();

    }
}

Eaven如果这个线程有点老了我想知道以下解决方案是否存在一些错误:

package service;

import java.io.File;

public class FileService {

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

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

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

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

while (!f.mkdirs()) {}

简单,但它的工作原理。

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM