如何为字符串到文件句柄的Map创建清理器?

icomxhvb  于 2021-07-06  发布在  Java
关注(0)|答案(1)|浏览(399)

我有一节课

class Something {
    private Map<String, RandomAccessFile> map = new LinkedHashMap<>() {
        @Override
        protected boolean removeEldestEntry(...) { also closes the file if it returns true; }

        @Override
        public RandomAccessFile remove(Object filename) { closes the file too; }
    };
}

这是文件名到文件句柄的Map。其思想是保持一些文件处于打开状态,以便something类的成员函数可以写入任何打开的文件。
现在我想重写finalize,以便当对象超出范围时,我们关闭所有文件。但是finalize被否决了。因此,我们的想法是调用cleaner.register(map,runnable),其中runnable获取“map”中的所有randomaccessfile并关闭每个文件。i、 例如 cleaner.register(map, () -> map.values().forEach(close file)) . 但是这使得runnable有一个对Map的引用,所以Map永远不会变得不可访问-类linkedhashmap.linkedvalues不是静态类。
有没有办法做到这一点?

toe95027

toe950271#

必须将其可访问性应控制清理的对象与清理所需的数据分开。
对你来说 Something 示例将确定文件是否仍在使用中,而map示例可以在清理操作期间使用,前提是您保持map私有并且从不分发它。
还有一些要点需要注意。文件 Cleaner 说:
清理操作可以是lambda,但是非常容易通过引用要清理的对象的字段来捕获对象引用,从而防止对象变得不可访问。如上所述,使用静态嵌套类可以避免意外地保留对象引用。
使用lambda表达式时 () -> map.values().forEach(close file)) 以及 map 是当前 Something 示例,您已隐式捕获 this . 但是,即使您修复了cleaner,让它只引用map示例,也有一个问题,即map本身是的一个匿名子类 LinkedHashMap 匿名内部类总是有一个对外部示例的隐式引用。
您可以通过在静态上下文中执行初始化来修复这两个问题:

class Something {
    private Map<String, RandomAccessFile> map = initializeMapAndCleaner(this);

    static final Cleaner CLEANER = Cleaner.create();

    static Map<String, RandomAccessFile> initializeMapAndCleaner(Object instance) {
        LinkedHashMap<String, RandomAccessFile> map = new LinkedHashMap<>() {
            @Override
            protected boolean removeEldestEntry(Map.Entry<String, RandomAccessFile> eldest) {
                /*also closes the file if it returns true;*/
            }

            @Override
            public RandomAccessFile remove(Object filename) { /*closes the file too;*/ }
        };
        CLEANER.register(instance, () -> map.values().forEach(f -> {/*close file*/}));
        return map;
    }
}

通过使用 static 方法,不能有任何隐式引用 this 以及 instance 参数已故意键入为 Object ,使意外访问map类或清理操作中的成员变得更加困难。
但请注意 LinkedHashMap 并不能保证所有的删除操作都要经过一次重写 remove 方法。此外,更换操作不使用 remove . 它可以为您的内部使用时 remove 是唯一的手术 put )那个 Something 类使用,但在这种情况下,使文件关闭调用者的职责也可能更干净。
也就是说,你根本不应该实施清洁。如果所有的清理工作都在进行,那就是关闭 RandomAccessFile ,这是过时的,因为在 RandomAccessFile 不关闭就无法访问。如果依赖那个清洁工感觉不舒服,那你就走对了。但是依靠 Something 他的清洁工一点也不好。
通常,变量作用域和对象可达性只是远程连接的。看到java能在对象仍在作用域中时完成它吗?或finalize()在Java8中为连接的实际问题调用强可达对象。当然,在java中何时调用finalize()方法中描述的计时问题?也适用于清洁剂。垃圾收集器可能永远不会运行,例如当内存足够时,也可能运行但不收集所有无法访问的对象,例如当它回收了足够的内存以便应用程序继续运行时。
如果要在变量作用域的末尾进行清理,可以使用try with resources语句。你只需要实现 AutoCloseable 或者它的一个子类型,提供 close() 方法关闭所有文件。

class Something implements Closeable { // java.io.Closeable is suitable here
    …

    @Override
    public void close() throws IOException {
        for(RandomAccessFile f: map.values()) f.close();
        map.clear();
    }
}

那么你可以很容易地说

try(Something sth = new Something(…)) {
    // use sth
}
// safely closed

相关问题