日常开发技巧(一):延迟删除文件

在一般的业务场景中,文件在使用完成后,会立即删除,代码如下:

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定时器,也是可以的,还是得具体情况具体分析,毕竟适合自己的才是最好的。

我是银河架构师,十年饮冰,难凉热血,愿历尽千帆,归来仍是少年! 

如果文章对您有帮助,请举起您的小手,轻轻【三连】,这将是笔者持续创作的动力源泉。当然,如果文章有错误,或者您有任何的意见或建议,请留言。感谢您的阅读!

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值