Потокобезопасный пул объектов с гарантией возврата существующего объекта, если существуют сильные ссылки? - PullRequest
3 голосов
/ 20 сентября 2010

Я пытаюсь расширить язык Clojure, чтобы расширить ACI-гарантированные ссылки на ACID-гарантированные ссылки (длительные ссылки).API должен просто вызывать (dref key value), где key - это строка ключа, который будет использоваться в базовом хранилище данных (BDB JE в моей текущей реализации), а value - это объект, которым должен быть dref.инициализирован до.Если key уже существует в БД, вместо него используется сохраненное значение.

Можно создать несколько dref с одним и тем же ключом, и их необходимо синхронизировать, т. Е. Если участвует один dref с ключом "A"в транзакции, где она записывается или читается с (ensure), все другие dref с ключом «A» должны быть синхронизированы с транзакцией: блокировки чтения и блокировки записи должны использоваться для наложения порядка на транзакции, включающие эти drefs.В более широком смысле, хотя в одном и том же ключе может быть несколько dref в памяти, все эти dref с этим ключом являются одним логическим объектом.

По очевидным причинам гораздо проще просточто этот единственный логический dref реализован с одним конкретным dref в памяти.Таким образом, нечего синхронизировать.Как мне это сделать?

Очевидный ответ - использовать пул объектов с ключом key .Затем Clojure вызовет статический метод getInstance(key,value) для извлечения из пула, если он существует, и создаст его и заполнит пул, если нет.Проблема с этим подходом состоит в том, что нет простого способа заставить Clojure выпустить объект, когда это будет сделано.Город утечки памяти.Я должен убедиться, что любой объект с сильными ссылками на него не будет собран и что они существуют в пуле.Было бы катастрофично, если пул потерял бы ссылки на логические dref, которые все еще используются, так как другой процесс мог бы создать новый dref с тем же ключом, и он не был бы безопасен в транзакционном отношении с другим dref с тем же ключом.

Так что мне нужна какая-то версия WeakHashMap или что-то с использованием не сильных ссылок (я бы предпочел SoftReference s для немного большего нежелания со стороны GC).Итак:

  1. Если я использую HashMap<String,SoftReference<DRef>>, как я могу гарантировать, что на карте будут удалены записи, если будет собрано значение записи (SoftReference)?Какой-то поток демона?
  2. Как сделать потокобезопасным пул для GC?Или мне не нужно беспокоиться об этом, поскольку сборщик мусора работает на уровне SoftReference, а мой поток демона будет работать на уровне Map?
  3. Что касается связанных примечаний, как мне это сделать?убедиться, что поток демона запущен?Есть ли способ, которым он может остановиться, не выдав исключение, которое приведет к аварийному завершению всего JVM, если он не будет обработан?Если так, как я могу контролировать и при необходимости запустить новый?

Ответы [ 3 ]

1 голос
/ 21 сентября 2010

Psssst .... вы воссоздаете распределенные общие объекты Терракоты. Внутренние элементы 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) Возможно, вам поможет выступление Боба Ли Призрак на виртуальной машине - это лучшее, что я нашел для такого рода вещей. SoftReferences и GC (и финализаторы) могут быть довольно хитрыми.

3) Google для необработанных обработчиков исключений ...

1 голос
/ 21 сентября 2010

Вы пробовали google-collection?

У них есть MapMaker, который предоставляет варианты одновременных хэш-карт с программными / слабыми клавишами и значениями. Одной из проблем является то, что равенство для слабых / мягких клавиш - это идентичность, что раздражает, но, возможно, не слишком много, если ключ является строкой.

Другие библиотеки делают то, во что я верю (org.apache.commons.collections, но я никогда не использовал их).

0 голосов
/ 21 сентября 2010

Простой ответ - это, вероятно, просто Collections.SynchronizedMap(new WeakHashMap()) - хотя это само по себе не дает итеративной итерации, безопасной для потоков.

1) Вы можете реализовать Map<K, V> самостоятельно и делегировать ConcurrentHashMap<K, SoftReference<V> >.Вы можете поместить свой SoftReferences в ReferenceQueue и либо использовать поток демона для удаления ссылок с вашей карты, либо просто проверить свой ReferenceQueue до / после каждой операции (или каждой n-й операции и т. Д.).

2) GC будет только аннулировать ваши ссылки - вам не нужно беспокоиться о том, что это портит вашу карту, поэтому нет проблем с многопоточностью.

3) Вы можете посмотреть, как AWT-EventQueueуправляетсяНо:

- ваш поток демона, вероятно, будет достаточно простым, чтобы не выдавать неожиданные исключения

- если вас это беспокоит, вы можете обернуть мясо вашего потока демона в

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

, который будет работать вечно, если только вы не получите сообщение об ошибке (в этом случае у вас возникнут более серьезные проблемы).

...