How to use mkdirs in a thread safe manner in Java?

前端 未结 5 2258
执念已碎
执念已碎 2021-02-14 17:51

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

相关标签:
5条回答
  • 2021-02-14 18:33

    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.

    0 讨论(0)
  • 2021-02-14 18:41

    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.

    0 讨论(0)
  • 2021-02-14 18:48

    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();
    
        }
    }
    
    0 讨论(0)
  • 2021-02-14 18:49

    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();
        }
    }
    
    0 讨论(0)
  • 2021-02-14 18:50

    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.

    0 讨论(0)
提交回复
热议问题