3

我正在尝试扩展 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使用非强引用的一些版本或一些东西(我更喜欢SoftReferences 让 GC 更不情愿)。所以:

  1. 如果我使用 a HashMap<String,SoftReference<DRef>>,如果收集到条目(SoftReference)的值,如何确保映射将逐出条目?某种守护线程?
  2. 如何使 GC 的池线程安全?还是我不必担心,因为 GC 在SoftReference级别上运行,而我的守护线程将是在Map级别上运行的那个?
  3. 在相关说明中,如何确保守护线程正在运行?有没有什么方法可以停止而不抛出异常,如果没有被捕获,整个 JVM 会崩溃?如果是这样,我如何监控并在需要时启动一个新的?
4

3 回答 3

1

你试过谷歌收藏吗?

他们有一个 MapMaker,可以提供具有软/弱键和值的并发散列映射的变体。一个问题是弱/软键的相等性是身份,如果键是字符串,这很烦人但可能不会太多。

我相信其他库会这样做(org.apache.commons.collections,但我从未使用过它们)。

于 2010-09-20T21:00:15.553 回答
1

Psssst....您正在重新创建 Terracotta 的分布式共享对象。Terracotta 的内部看起来与此非常相似,尽管它们(在 DSO 中)依赖于在加载时使用字节码操作来拦截对字段的所有读取和写入,而在 Clojure 中则要容易得多。

如果您想查看 Terracotta 实现,ClientObjectManager ( http://svn.terracotta.org/svn/tc/dso/trunk/code/base/dso-l1/src/com/tc/object/ ) 是管理共享对象的主要客户端类。查看 pojoToManaged 并查看 TCObjectImpl 中的一些相关代码。

1,2) 您可能会发现 Bob Lee 的演讲The Ghost in the Virtual Machine很有帮助 - 这是我找到的此类内容的最佳参考。SoftReferences 和 GC(和终结器)可能有点棘手。

3)谷歌未捕获的异常处理程序......

于 2010-09-21T01:05:27.507 回答
0

简单的答案可能只是Collections.SynchronizedMap(new WeakHashMap())- 尽管它本身并不能为您提供线程安全的迭代。

1)您可以实现Map<K, V>自己,并委托给ConcurrentHashMap<K, SoftReference<V> >. 您可以将您的SoftReferences放在 a 中ReferenceQueue,并使用守护线程从您的地图中删除引用,或者仅ReferenceQueue在每个操作(或每个第 n 个操作等)之前/之后检查您的引用。

2) GC 只会清除您的引用 - 您不必担心它会与您的地图混淆,因此没有线程问题。

3)您可以查看 AWT-EventQueue 的管理方式。但:

- 你的守护线程可能足够简单,不会抛出意外的异常

- 如果你担心它,你可以把你的守护线程的肉包起来

for (;;) {
  try {
    //daemon thread loop here
  } catch (Exception ex) {
    //log it, any other possible cleanup
  }
}

除非你得到一个错误,否则它将永远运行(在这种情况下你有更大的问题。)

于 2010-09-20T21:23:55.167 回答