mkdirs() で問題が発生し、interwebs をいじってみると、mkdirs() にはスレッド セーフの問題があるように思えます。
複数のスレッドが同様のファイル構造を作成しようとしている可能性がある場合に、ディレクトリが適切に作成されるようにする方法はありますか?
ありがとう
(私の場合、Androidでこれを使用します)
mkdirs() で問題が発生し、interwebs をいじってみると、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();
}
}
ディレクトリがすでに存在する場合、メソッドは早期に戻ります。存在しない場合は、1 つのスレッドのみが作成を試みます。
ディレクトリの作成はすべて、すべてをシリアル化するワーカー スレッドで行います。Looper
aと aを使用して、その呼び出し mkdirs をワーカー スレッドHandler
にポストしやすくすることができます。Runnables
ディレクトリの作成が完了したら、 Looper.quit() を呼び出して、最後に投稿された を処理した後にスレッドを終了できますRunnable
。のドキュメントにLooper
は、これがいかに簡単かを示すサンプル コードがあります。
考えられる解決策の 1 つは、1 つのインスタンスのみを保証し、独自のスレッドで実行する 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();
}
}
わかりました、これがしばらくアクティブでないことは知っていますが、おそらく簡単な解決策があると思いました. 質問のコメントでリンクした記事は、ディレクトリが作成されていないことが唯一の問題であることを示しているようです。解決策はこれを行うことでした:
if (!f.mkdirs()) {
f.mkdirs();
}
ただし、これは非効率的で、まだ問題が発生する可能性があります。では、単純にこれを実行してみませんか。
while (!f.mkdirs()) {}
シンプルですが、うまくいきます。
編集:少し考えた後、その例は忘却に遅れ、スレッドロックを引き起こす可能性があります。したがって、これはより良いアイデアかもしれません:
while (!f.mkdirs()) { Thread.yield(); }
もちろん、スレッドロックを引き起こす可能性のあるスレッドにいる場合、およびそれが優先度の高い状況でない限り、これは推奨されます。これを出すだけです。
このスレッドが少し古い場合でも、次の解決策に何か問題があるのではないかと思います。
package service;
import java.io.File;
public class FileService {
public static synchronized boolean mkdirs( File dir ) {
return dir.mkdirs();
}
}