问题
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)
回答1:
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.
回答2:
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.
回答3:
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.
回答4:
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();
}
}
回答5:
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();
}
}
来源:https://stackoverflow.com/questions/5189534/how-to-use-mkdirs-in-a-thread-safe-manner-in-java