我有一个简单的java程序,它创建一系列存储在本地tmp目录中的临时文件。我添加了一个简单的关闭钩子,它遍历所有文件并删除它们,然后在退出程序之前删除tmp目录。这是代码:
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
File tmpDir = new File("tmp/");
for (File f : tmpDir.listFiles()) {
f.delete();
}
tmpDir.delete();
}
}));
我的问题是创建这些文件的线程可能在启动关闭挂钩时没有终止,因此,可能会在调用listFiles()
之后创建一个文件。这会导致tmp目录不被删除。我已经想出了2个黑客:
Hack#1:
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
File tmpDir = new File("tmp/");
while (!tmp.delete()){
for (File f : tmpDir.listFiles()) {
f.delete();
}
}
}
}));
Hack#2:
Runtime.getRuntime().addShutdownHook(new Thread(new Runnable() {
@Override
public void run() {
try{
Thread.sleep(1000);
} catch(InterruptedException e){
e.printStackTrace();
}
File tmpDir = new File("tmp/");
for (File f : tmpDir.listFiles()) {
f.delete();
}
tmpDir.delete();
}
}));
两者都不是特别好的解决方案。理想的是让关闭钩子等到所有线程都终止后再继续。有谁知道这是否可以做到?
答案 0 :(得分:7)
在关闭程序之前,只需跟踪所有正在运行的线程,然后.join()
。
这是问题标题的答案,因为ewok说他不能使用.deleteOnExit()
答案 1 :(得分:2)
泰勒说的话,但更详细一点:
答案 2 :(得分:2)
更新: Tyler Heiks准确地指出 deleteOnExit()不是有效的解决方案,因为OP尝试了它并且它不起作用。我正在提供替代解决方案。它又是间接的,但主要是因为使用线程和ShutdownHook的原始设计存在致命缺陷。
使用 finally 块删除临时文件。
依靠ShutdownHooks进行资源管理是一个非常糟糕的主意,并且使代码很难在更大的系统中进行组合或重用。将资源从线程交给线程是一个更糟糕的想法。像文件和流这样的资源是线程之间共享的最危险的东西。可能很少从中获益,每个线程使用库的 createTempFile 方法独立获取临时文件并使用 try / finally <管理它们的使用和删除更有意义/强>
处理系统上临时文件的惯例是将它们视为块框,其中:
如果您手动滚动代码以自行创建和命名临时文件,则上面的第三个很难实现。它可能很脆弱,在最糟糕的时候会失败(任何人都可以在凌晨3点开始寻呼?)。
您提供的算法可以删除由巧合共享同一父目录的其他进程创建的文件。对于其他计划的稳定性来说,这不太可能是一件好事。
以下是高级流程:
这类似于 InputStream 或其他需要手动管理的资源。
显式资源管理的一般模式( AutoCloseable 和 try-with-resources 不可用时)如下所示。
Resource r = allocateResource();
try {
useResource(r);
} finally {
releaseResource(r);
}
对于路径,它看起来像这样:
Path tempDir = Paths.get("tmp/);
try {
Path p = Files.createTempFile(tempDir, "example", ".tmp");
try {
useTempFile(f);
} finally {
Files.delete(f);
}
} finally {
Files.delete(tempDir);
}
在Java 7之前的版本中,与文件的使用如下所示:
File tempDir = new File("tmp/");
try {
File f = File.createTempFile(tempDir, "example", ".tmp");
try {
useTempFile(f);
} finally {
if (!f.delete()) {
handleFailureToDeleteTempFile(f);
}
}
} finally {
if (!tempDir.delete()) {
handleFailureToDeleteTempDir(tempDir);
}
}