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

发布于 2024-10-19 23:52:37 字数 146 浏览 5 评论 0原文

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

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

谢谢

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

After experiencing issues with mkdirs() and poking around the interwebs, I get the impression that there are thread safety issues with mkdirs().

Is there a way to ensure the directories are properly created when it is possible that multiple threads might be trying to create similar file structures?

Thanks

(In my case I will be using this on Android)

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(5

躲猫猫 2024-10-26 23:52:37

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

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

I'm not sure if Android supports the concurrent package but here is my take:

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

The method returns early if the directory already exists. If it doesn't exist, only one thread will try to create it.

梦醒时光 2024-10-26 23:52:37

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

Do all your directory creation in a worker thread that serializes everything. You can use a Looper and a Handler to make it easy to post Runnables that call mkdirs to your worker thread. When you're done making directories, you can call Looper.quit() to end the thread after it processes the last posted Runnable. The documentation for Looper has sample code that shows how near to trivial this is to do.

半﹌身腐败 2024-10-26 23:52:37

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

    }
}

One possible solution would be a MkDirService (illustrated below) that guarantees only one instance and runs in it's own thread. Making use of BlockingQueue.

First the Service:

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

The the Test:

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

    }
}
森林迷了鹿 2024-10-26 23:52:37

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

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

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

while (!f.mkdirs()) {}

简单,但有效。

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

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

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

Okay, I know this has been inactive for a while, but I thought perhaps there was a simple solution. The article you linked in the comments on the question seems to indicate that the only problem is directories not being created. The solution there was to do this:

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

However, that seems inefficient and can still have problems. So, why not simply do this:

while (!f.mkdirs()) {}

Simple, but it works.

EDIT: After thinking a bit, that example may lag to oblivion and could cause thread lock. So, this might be a better idea:

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

Of course, that would only be recommended if you're in a thread that could cause thread lock, and as long as it's not a high-priority situation. Just putting this out there.

盗心人 2024-10-26 23:52:37

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

package service;

import java.io.File;

public class FileService {

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

Eaven if this thread is a bit older I wonder if there is somethink wrong with the following solution:

package service;

import java.io.File;

public class FileService {

    public static synchronized boolean mkdirs( File dir ) {
        return dir.mkdirs();
    }
}
~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文