1. 用虚引用监控对象,当对象被回收后,删除虚引用对象关联的临时目录

1. 应用场景:

文件上传的时候,如果文件过大(可以自己设置),为了降低内存消耗会在系统某个目录下生成 临时文件,程序中 处理的时候,只会带着 这个临时文件 的目录(而不是字节数组),但是随着时间的推移,临时文件越来越大,对磁盘也是一种很大的压力,这种时候就需要虚引用和引用队列了,当  一个对象A刚创建的时候,将这个对象和一个虚引用对象关联,同时将虚引用对象放到 引用队列里,当对象A 被垃圾回收的时候,就可以从引用队列里获取 虚引用对象,虚引用对象 可以定义 文件 全名称 属性,进行 对临时文件的删除操作。

2. 代码如下:

工具类:

package com.example.demo.clean;


import java.io.File;
/**
 * @program: springboot_01
 * @description:
 * @author: guoyiguang
 * @create: 2021-08-07 11:12
 **/
public class FileCleaner {


    /**
     * The instance to use for the deprecated, static methods.
     */
    static final FileCleaningTracker theInstance = new FileCleaningTracker();

    public static void addTracker(String path, Object marker) {
        theInstance.addTracker(path, marker);
    }



    public static FileCleaningTracker getInstance() {
        return theInstance;
    }
    //简单测试
    public static void main(String[] args){
        // E:\shan
//        getInstance().track("E:\\shan\\tt.txt", new Object());
//        getInstance().track("E:\\shan\\ttt.txt",new Object());
//        System.gc();
//        System.exit(0);
    }
}
package com.example.demo.clean;

/**
 * @program: 文件跟踪类(一个文件和 一个对象绑定,对象被垃圾回收进行相应操作)
 * @description: 2、FileCleaningTracker类,
 * @author: guoyiguang
 * @create: 2021-08-07 11:09
 **/

import java.io.File;
import java.io.IOException;
import java.lang.ref.PhantomReference;
import java.lang.ref.ReferenceQueue;
import java.util.Collection;
import java.util.Vector;
public class FileCleaningTracker {

    // 引用队列
    ReferenceQueue q = new ReferenceQueue();

    // synchronized
    final Collection  trackers = new Vector();
    /**
     * Whether to terminate the thread when the tracking is complete.
     */
    volatile boolean exitWhenFinished = false;
    // 用于删除文件的线程
    Thread reaper;






    /** 
    * @Description:  synchronized 关键字 分布式的情况下 也是可以用的(如下的这种情况就可以用)
    * @Param:  
    * @return:  
    * @Author: guoyiguang
    * @Date:  
    */ 
    public synchronized void addTracker(String path, Object marker) {

        if (path == null) {
            throw new NullPointerException("The path must not be null");
        }
        // synchronized block protects reaper
        if (exitWhenFinished) {
            throw new IllegalStateException("No new trackers can be added once exitWhenFinished() is called");
        }
        if (reaper == null) {
            reaper = new Reaper();
            // 开启线程
            reaper.start();
        }
        // 将 marker 和  引用队列绑定,marker 被回收后 ,进行删除文件
        // 放到 trackers 集合里  是为了方便监控
        trackers.add(new Tracker(path, marker, q));
    }


    /** 
    * @Description:  清理临时文件的守护线程
    * @Param:  
    * @return:  
    * @Author: guoyiguang
    * @Date:  
    */ 
    private final class Reaper extends Thread {

            Reaper() {
                super("File Reaper");
                setPriority(Thread.MAX_PRIORITY);
                // 设置为守护线程
                setDaemon(true);
            }

            public void run() {
                // thread exits when exitWhenFinished is true and there are no more tracked objects
                while (exitWhenFinished == false || trackers.size() > 0) {
                    Tracker tracker = null;
                    try {
                        // 当 真实对象被删除 ,引用 队列 弹出  虚引用 对象(有 path 属性 知道 删除哪个 文件)
                        tracker = (Tracker) q.remove();
                    } catch (Exception e) {
                        continue;
                    }
                    if (tracker != null) {
                        // 删除文件
                        try {
                            FileDeleteStrategy.doDelete(new File(tracker.path));
                        } catch (IOException e) {
                            e.printStackTrace();
                        }
                        tracker.clear();
                        // 记录
                        trackers.remove(tracker);
                    }
                }
            }

    }



        //虚引用
    private static final class Tracker extends PhantomReference {
            // 要删除的文件路径
            private final String path;
            Tracker(String path, Object marker, ReferenceQueue queue) {
                // 将 Tracker  虚引用 对象 的 marker对象  和 引用队列绑定 , 监控 marker 对象的生命周期
                super(marker, queue);
                this.path = path;
            }

        }


}

 

package java.lang.ref;
public class PhantomReference<T> extends Reference<T> {

    /**
     * Returns this reference object's referent.  Because the referent of a
     * phantom reference is always inaccessible, this method always returns
     * <code>null</code>.
     *
     * @return  <code>null</code>
     */
    public T get() {
        return null;
    }

    /**
     * Creates a new phantom reference that refers to the given object and
     * is registered with the given queue.
     *
     * <p> It is possible to create a phantom reference with a <tt>null</tt>
     * queue, but such a reference is completely useless: Its <tt>get</tt>
     * method will always return null and, since it does not have a queue, it
     * will never be enqueued.
     *
     * @param referent the object the new phantom reference will refer to
     *  T 是  和虚引用对象关联的 对象
     * @param q the queue with which the reference is to be registered,
     *          or <tt>null</tt> if registration is not required
     */
    public PhantomReference(T referent, ReferenceQueue<? super T> q) {
        super(referent, q);
    }

}
package com.example.demo.clean;


import java.io.File;
import java.io.IOException;
/**
 * @program: 删除文件的工具类
 * @description:
 * @author: guoyiguang
 * @create: 2021-08-07 11:06
 **/
public class FileDeleteStrategy {
    public static final FileDeleteStrategy NORMAL = new FileDeleteStrategy("Normal");
    private final String name;
    protected FileDeleteStrategy(String name) {
        this.name = name;
    }



    public static boolean doDelete(File fileToDelete) throws IOException {

        if (fileToDelete == null || fileToDelete.exists() == false){
            System.out.println("文件"+fileToDelete.getPath()+ "不存在");
            return false;
        }
        System.out.println("准备 删除文件"+fileToDelete.getPath());
        boolean delete = fileToDelete.delete();
        System.out.println(" 删除文件 result  " +   delete);
        return delete;
    }



}



测试方法:

 @SneakyThrows
    @Test
    public void testList32() {

        // windows  C:\Users\dell\AppData\Local\Temp
        File location =  new  File(System.getProperty("java.io.tmpdir"));

        // sizeThreshold: The threshold (阈值 ) above which uploads will be stored on disk.
        // 封装了临时文件的全路径(包括文件名)
        FileItem item = new DiskFileItem("hh", "image/png",
                false, "fileName.jpg", 1, location);
        OutputStream outputStream = null;
        try {
            //  生成 临时文件 File 对象 和 临时文件名称 (以  .temp  后缀命名)
            outputStream = item.getOutputStream();

            // 写入内容
            // 查看 实际走的OutputStream 接口的  哪个实现类
            // class org.apache.tomcat.util.http.fileupload.DeferredFileOutputStream
            System.out.println(outputStream.getClass());
            // DeferredFileOutputStream  父类的 write 方法
            outputStream.write("DCTK_2021080300011234565432123456543234565432234543234543234543234554323456".getBytes(StandardCharsets.UTF_8));
            ApplicationPart part = new ApplicationPart(item, location);


            MultipartFile multipartFile =  new StandardMultipartFile2(part,"hh");
            System.out.println(multipartFile);

            // multipartFile.getInputStream();

            String s = new String(multipartFile.getBytes());
            System.out.println(s);
            // 删除临时文件(断点 知道  DeferredFileOutputStream  里面 有 path 属性)
            DeferredFileOutputStream outputStream1 = (DeferredFileOutputStream) outputStream;
            System.out.println(" 绑定的目录 为:");
            System.out.println( ((DeferredFileOutputStream) outputStream).getFile().getPath());
            // 生成虚引用 对象
            FileCleaner.getInstance().addTracker(outputStream1.getFile().getPath(), multipartFile);

        } catch (IOException e) {
            e.printStackTrace();
        }finally {
            try {
                //  必须关闭 流 ;否则对象回收不了
                outputStream.close();
            } catch (IOException e) {
                e.printStackTrace();
            }
        }

        System.gc();

        System.exit(0);

    }

代码说明:

  FileItem item = new DiskFileItem("hh", "image/png",
                false, "fileName.jpg", 1, location);
      
  //  生成 临时文件 File 对象 和 临时文件名称 (以  .temp  后缀命名)
   OutputStream   outputStream = item.getOutputStream();

生成临时文件:

 @Override
    public OutputStream getOutputStream()
        throws IOException {
        if (dfos == null) {
            File outputFile = getTempFile();
            dfos = new DeferredFileOutputStream(sizeThreshold, outputFile);
        }
        return dfos;
    }
protected File getTempFile() {
        if (tempFile == null) {
            File tempDir = repository;
            if (tempDir == null) {
               // java  系统目录
                tempDir = new File(System.getProperty("java.io.tmpdir"));
            }
            // 格式化 临时文件名称
            // UID =  UUID.randomUUID().toString().replace('-', '_')
            // getUniqueId 生成唯的数
            String tempFileName =
                    String.format("upload_%s_%s.tmp", UID, getUniqueId());

            // 某个目录下创建某个文件
            tempFile = new File(tempDir, tempFileName);
        }
       // 返回临时文件
        return tempFile;
    }

测试效果:

 

 

 查看写入的数据:

 手动调用:

System.gc();

后:

文件被删除:

 查看日志:

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值