在一般的业务场景中,文件在使用完成后,会立即删除,代码如下:
public void normalDelete() {
String fileName = UUID.randomUUID().toString() + ".txt";
File file = new File("D:\\123", fileName);
try {
FileOutputStream fileOutputStream = new FileOutputStream(file, true);
for (int i = 0; i < 10; i++) {
fileOutputStream.write(("" + i + "\r\n").getBytes(StandardCharsets.UTF_8));
}
fileOutputStream.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
file.delete();
}
}
但是,如果遇到特殊情况,如文件需要传输、上传到别的平台/系统,完成后尚需校验原始文件和接收文件的MD5以确定完整性。一旦立即删除的话,则此时校验MD5会报文件不存在的异常。
因此,需要延迟删除文件。
实现方式有两种,一种是Timer
定时器,一种是ScheduledThreadPoolExecutor
。
先使用Timer
定时器实现。
private final Timer timer = new Timer("default-timer");
public void timerDelayDelete() {
String fileName = UUID.randomUUID().toString() + ".txt";
File file = new File("D:\\123", fileName);
try {
FileOutputStream fileOutputStream = new FileOutputStream(file, true);
for (int i = 0; i < 10; i++) {
fileOutputStream.write(("" + i + "\r\n").getBytes(StandardCharsets.UTF_8));
}
fileOutputStream.close();
System.out.println("文件 " + file.getAbsolutePath() + " 已创建" + LocalDateTime.now());
} catch (IOException e) {
e.printStackTrace();
} finally {
timer.schedule(new DeleteLocalTempFileTask(file), 30000);
}
while (true) ;
}
class DeleteLocalTempFileTask extends TimerTask {
private File LocalTempFile;
public DeleteLocalTempFileTask(File localTempFile) {
this.LocalTempFile = localTempFile;
}
public void run () {
if (LocalTempFile.exists()) {
LocalTempFile.delete();
System.out.println("文件" + LocalTempFile.getAbsolutePath() + "已删除" + LocalDateTime.now());
}
}
}
延迟30000毫秒,即30秒删除。此延迟时间为示例时间,实际的延迟时间定义还需要以具体业务场景来考量。
代码执行之后,会在30秒后删除文件。
再使用ScheduledThreadPoolExecutor
实现。
private final ScheduledThreadPoolExecutor deleteLocalTempFileExecutor = new ScheduledThreadPoolExecutor(10);
public void scheduledDelayDelete() {
String fileName = UUID.randomUUID().toString() + ".txt";
File file = new File("D:\\123", fileName);
try {
FileOutputStream fileOutputStream = new FileOutputStream(file, true);
for (int i = 0; i < 10; i++) {
fileOutputStream.write(("" + i + "\r\n").getBytes(StandardCharsets.UTF_8));
}
fileOutputStream.close();
System.out.println("文件 " + file.getAbsolutePath() + " 已创建" + LocalDateTime.now());
} catch (IOException e) {
e.printStackTrace();
} finally {
deleteLocalTempFileExecutor.schedule(new DeleteLocalTempFileTask(file), 30, TimeUnit.SECONDS);
}
while (true) ;
}
class DeleteLocalTempFileTask extends TimerTask {
private File LocalTempFile;
public DeleteLocalTempFileTask(File localTempFile) {
this.LocalTempFile = localTempFile;
}
public void run () {
if (LocalTempFile.exists()) {
LocalTempFile.delete();
System.out.println("文件" + LocalTempFile.getAbsolutePath() + "已删除" + LocalDateTime.now());
}
}
}
执行后,也会在30秒后删除掉文件。
这两种方式,都是可以。但是,会有一些不同:
Timer
只有一个线程在执行,而ScheduledThreadPoolExecutor
为线程池,可以配置多个执行线程。
Timer
对系统时钟的变化比较敏感,而ScheduledThreadPoolExecutor
则不会。
如果Timer
定义为公共属性,如果遇到TimerTask
中发生异常且抛出的情况,会杀死此Timer
执行线程,导致后续的任务计划不会被执行。而ScheduledThreadPoolExecutor
线程池中的执行线程,互相独立,互不影响。某个执行任务的异常、取消,并不会影响其他任务计划。
关于第三点,特别举例说明一下:
public void timerDelayDeleteException() throws InterruptedException {
TimerTask task = new TimerTask() {
@Override
public void run() {
System.out.println("Hello World 1.");
System.out.println(LocalDateTime.now());
int i = new Random().nextInt(10);
if (i == 3) {
throw new RuntimeException("111");
}
}
};
Timer timer = new Timer("timer");
timer.scheduleAtFixedRate(task,10l, 1000l);
timer.schedule(new TimerTask() {
@Override
public void run() {
System.out.println("Hello World 2.");
}
}, 10l, 1000l);
while (true) ;
}
按正常情况,会每隔1秒打印一次Hello World 1、Hello World 2。
但是,在打印Hello World 1的执行线程中,会随机报错,如果随机数等于3,则抛出异常。此时,Timer线程会被杀死,后续不会再打印Hello World 1、Hello World 2。
执行之后,确实如此。
不过,如果只是单次执行某次任务,每次执行完毕后再取消Timer定时器,也是可以的,还是得具体情况具体分析,毕竟适合自己的才是最好的。
我是银河架构师,十年饮冰,难凉热血,愿历尽千帆,归来仍是少年!
如果文章对您有帮助,请举起您的小手,轻轻【三连】,这将是笔者持续创作的动力源泉。当然,如果文章有错误,或者您有任何的意见或建议,请留言。感谢您的阅读!