Как использовать mkdirs потокобезопасным способом в Java? - PullRequest
9 голосов
/ 04 марта 2011

После возникновения проблем с mkdirs () и работы с сетями, у меня складывается впечатление, что с mkdirs () возникают проблемы с безопасностью потоков.

Есть ли способ убедиться, что каталоги созданы правильно, когда онВозможно ли, что несколько потоков пытаются создать похожие файловые структуры?

Спасибо

(В моем случае я буду использовать это на Android)

Ответы [ 5 ]

5 голосов
/ 28 апреля 2011

Я не уверен, поддерживает ли 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();
    }
}

Метод возвращается рано, если каталог уже существует. Если он не существует, только один поток попытается создать его.

3 голосов
/ 04 марта 2011

Все ваши каталоги создаются в рабочем потоке, который все сериализует. Вы можете использовать Looper и Handler, чтобы упростить публикацию Runnables с вызовом mkdirs в вашем рабочем потоке. Когда вы закончите создавать каталоги, вы можете вызвать Looper.quit (), чтобы завершить поток после того, как он обработает последнюю опубликованную Runnable. Документация для Looper содержит пример кода, который показывает, насколько это тривиально.

2 голосов
/ 04 марта 2011

Одним из возможных решений будет 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();

    }
}
1 голос
/ 22 сентября 2013

Боже, если этот поток немного старше, мне интересно, если что-то не так со следующим решением:

package service;

import java.io.File;

public class FileService {

    public static synchronized boolean mkdirs( File dir ) {
        return dir.mkdirs();
    }
}
1 голос
/ 02 августа 2011

Хорошо, я знаю, что это было неактивно некоторое время, но я подумал, что, возможно, было простое решение. Статья, которую вы связали в комментариях к вопросу, похоже, указывает на то, что единственная проблема - это каталоги , а не , которые создаются. Решение было сделать это:

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

Однако это кажется неэффективным и может иметь проблемы. Итак, почему бы просто не сделать это:

while (!f.mkdirs()) {}

Просто, но работает.

РЕДАКТИРОВАТЬ: Подумав немного, этот пример может отстать от забвения и может привести к блокировке потока. Так что это может быть лучшей идеей:

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

Конечно, это будет рекомендовано только в том случае, если вы находитесь в потоке, который может вызвать блокировку потока, и при условии, что это не высокоприоритетная ситуация. Просто поместил это там.

...