我正在尝试扩展 Clojure 语言以将 ACI 保证的 refs 扩展为 ACID 保证的 drefs(持久参考)。API 是简单的调用(dref key value)
,其中key
是要在底层数据存储中使用的键的字符串(在我当前的实现中是 BDB JE),并且value
是应该初始化 dref 的对象。如果key
数据库中已经存在,则使用存储的值。
可以使用相同的密钥创建多个 dref,并且它们需要同步,即,如果具有密钥“A”的一个 dref 参与了使用 写入或读取它的事务,则(ensure)
具有密钥“A”的所有其他 dref 必须是事务性的同步:必须使用读锁和写锁来对涉及这些 dref 的事务进行排序。从更大的意义上说,尽管内存中可能有多个具有相同键的 dref,但所有具有该键的 dref 都是一个逻辑对象。
出于显而易见的原因,简单地确保这个单个逻辑 dref 是使用单个具体的内存中 dref 实现的要容易得多。这样就没有什么要同步的了。我该怎么做呢?
显而易见的答案是使用 key 上的对象池。如果池存在, Clojure 将调用静态getInstance(key,value)
方法从池中检索,如果不存在,则创建它并填充池。这种方法的问题是没有简单的方法让 Clojure 在完成时释放对象。内存泄漏城市。我必须确保不会收集任何对其有强引用的对象,并且它们存在于池中。如果池丢失对仍在使用的逻辑 dref 的引用,那将是灾难性的,因为另一个进程可以使用相同的密钥创建一个新的 dref,并且对于具有相同密钥的另一个 dref,它在事务上是不安全的。
所以我需要WeakHashMap
使用非强引用的一些版本或一些东西(我更喜欢SoftReference
s 让 GC 更不情愿)。所以:
- 如果我使用 a
HashMap<String,SoftReference<DRef>>
,如果收集到条目(SoftReference)的值,如何确保映射将逐出条目?某种守护线程? - 如何使 GC 的池线程安全?还是我不必担心,因为 GC 在
SoftReference
级别上运行,而我的守护线程将是在Map
级别上运行的那个? - 在相关说明中,如何确保守护线程正在运行?有没有什么方法可以停止而不抛出异常,如果没有被捕获,整个 JVM 会崩溃?如果是这样,我如何监控并在需要时启动一个新的?