Это идеальное решение для API, которые не имеют механизма управления жизненным циклом, но который вы реализуете с помощью чего-то, что требует явного управления жизненным циклом.
В частности, любой вид API, который раньше просто использовал объекты в памяти, но который вы переопределили, используя соединение с сокетом или соединение с файлом с каким-либо другим, большим хранилищем резервных копий, может использовать PhantomReference для «закрытия» и очистки информации о соединении до того, как объект был GC'd, и соединение никогда не закрывалось, потому что не было никакого интерфейса API управления жизненным циклом, который вы могли бы иначе использовать.
Подумайте о переносе простой карты в базу данных. Когда ссылка на карту отбрасывается, нет явной операции «закрыть». Тем не менее, если вы реализовали запись через кеш, вы хотели бы иметь возможность завершать любые записи и закрывать сокетное соединение с вашей «базой данных».
Ниже приведен класс, который я использую для такого рода вещей. Обратите внимание, что ссылки на PhantomReferences должны быть нелокальными ссылками для правильной работы. В противном случае jit заставит их преждевременно встать в очередь, прежде чем вы выйдете из блоков кода.
import java.lang.ref.PhantomReference;
import java.lang.ref.Reference;
import java.lang.ref.ReferenceQueue;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* This class provides a way for tracking the loss of reference of one type of
* object to allow a secondary reference to be used to perform some cleanup
* activity. The most common use of this is with one object which might
* contain or refer to another object that needs some cleanup performed
* when the referer is no longer referenced.
* <p>
* An example might be an object of type Holder, which refers to or uses a
* Socket connection. When the reference is lost, the socket should be
* closed. Thus, an instance might be created as in
* <pre>
* ReferenceTracker trker = ReferenceTracker() {
* public void released( Socket s ) {
* try {
* s.close();
* } catch( Exception ex ) {
* log.log( Level.SEVERE, ex.toString(), ex );
* }
* }
* };
*
* Где-то могут быть такие звонки, как следующие.
*
* interface Holder {
* public T get();
* }
* class SocketHolder implements Holder {
* Socket s;
* public SocketHolder( Socket sock ) {
* s = sock;
* }
* public Socket get() {
* return s;
* }
* }
*
* Это определяет реализацию интерфейса Holder, который содержит
* ссылка на объекты Socket. Использование
trker
* объект выше, может включать использование метода для создания
* объекты и регистрация ссылок, как показано ниже.
*
* public SocketHolder connect( String host, int port ) throws IOException {
* Socket s = new Socket( host, port );
* SocketHolder h = new SocketHolder( s );
* trker.trackReference( h, s );
* return h;
* }
*
* Программное обеспечение, желающее использовать сокетное соединение и передать его
* используйте SocketHolder.get () для ссылки на экземпляр Socket во всех случаях.
* затем, когда все ссылки на SocketHolder будут удалены, сокет
* быть закрытым показанным методом
released(java.net.Socket)
* выше.
*
* Класс {@link ReferenceTracker} использует {@link PhantomReference} для первого аргумента как
* ключ к карте, содержащий ссылку на второй аргумент. Таким образом, когда
* экземпляр ключа освобожден, ссылка на ключ поставлена в очередь, может быть удалена из
* очередь, и используется для удаления значения из карты, которая затем передается
* вышел().
* /
открытый абстрактный класс ReferenceTracker {
/ **
* Экземпляр потока, который удаляет записи из справочной очереди, перезаписывает их по мере их появления.
* /
частный волатильный опрос RefQueuePoll;
/ **
* Экземпляр Logger, используемый для этого экземпляра. Это будет включать имя в качестве суффикса
* если этот конструктор используется.
* /
private static final Logger log = Logger.getLogger (ReferenceTracker.class.getName ());
/ **
* Имя, указывающее, какой экземпляр это для регистрации и другого разделения
* необходимые экземпляры.
* /
приватный финал String which;
/ **
* Создает новый экземпляр ReferenceTracker, используя переданное имя для дифференциации
* экземпляр в журналировании и реализации toString ().
* @param which Имя этого экземпляра для различения нескольких экземпляров в журнале и т. д.
* /
public ReferenceTracker (String which) {
this.which = which;
}
/ **
* Создает новый экземпляр ReferenceTracker без соответствующего имени.
* /
public ReferenceTracker () {
this.which = null;
}
/ **
* Предоставляет доступ к имени этого экземпляра.
* @return Название этого экземпляра.
* /
@Override
public String toString () {
если (который == ноль) {
return super.toString () + ": ReferenceTracker";
}
return super.toString () + ": ReferenceTracker [" + which + "]";
}
/ **
* Подклассы должны реализовывать этот метод. Это будет вызвано, когда все ссылки на
* связанный объект-держатель сброшен.* @param val Значение, переданное в качестве второго аргумента для соответствующего вызова {@link #trackReference (Object, Object) trackReference (T, K)}
* /
публичная аннотация освобождена (K val);
/ ** Очередь ссылок для ссылок на объекты-держатели * /
закрытый финал ReferenceQueuerefqueue = new ReferenceQueue ();
/ **
* Подсчет общего количества потоков, которые были созданы, а затем уничтожены, поскольку записи имеют
* был отслежен. При отсутствии отслеживаемых ссылок очередь не выполняется.
* /
закрытый финал AtomicInteger tcnt = new AtomicInteger ();
частный изменчивый логический запуск;
/ **
* Реализация потока, которая опрашивает {@link #refqueue} для последующего вызова {@link release (K)}
* поскольку ссылки на объекты T отбрасываются.
* /
закрытый класс RefQueuePoll расширяет тему {
/ **
* Номер потока, связанный с этим экземпляром. Там может быть кратко два случая
* этот класс, который существует в энергозависимой системе. Если это так, это значение будет
* быть видимым в некоторых журналах, чтобы отличить активные.
* /
закрытый финал int mycnt;
/ **
* Создает экземпляр этого класса.
* /
public RefQueuePoll () {
setDaemon (true);
setName (getClass (). getName () + ": ReferenceTracker (" + which + ")");
mycnt = tcnt.incrementAndGet ();
}
/ **
* Этот метод обеспечивает всю активность выполнения refqueue.remove()
* звонит, а затем звонит released(K)
, чтобы приложение выпустило
* необходимые ресурсы.
* /
public @Override void run () {
пытаться {
doRun ();
} catch (Throwable ex) {
log.log (сделано? Level.INFO: Level.SEVERE,
ex.toString () + ": остановка потока опроса phantom ref", ex);
} в конце концов {
бег = ложь;
}
}
private volatile boolean done = false;
private void doRun () {
пока (! сделано) {
Ссылка ref = null;
пытаться {
бег = истина;
ref = refqueue.remove ();
K ctl;
синхронизированный (refmap) {
ctl = refmap.remove (ref);
done = actCnt.decrementAndGet () == 0;
if (log.isLoggable (Level.FINE)) {
log.log (Level.FINE, "current act refs = {0}, mapsize = {1}", new Object [] {actCnt.get (), refmap.size ()});
}
if (actCnt.get ()! = refmap.size ()) {
Throwable ex = new IllegalStateException («количество активных ссылок и размер карты не синхронизированы»);
log.log (Level.SEVERE, ex.toString (), ex);
}
}
if (log.isLoggable (Level.FINER)) {
log.log (Level.FINER, "ссылка выпущена для: {0}, dep = {1}", новый объект [] {ref, ctl});
}
if (ctl! = null) {
пытаться {
освобожден (ctl);
if (log.isLoggable (Level.FINE)) {
log.log (Level.FINE, "освобожден зависимый объект: {0}", ctl);
}
} catch (RuntimeException ex) {
log.log (Level.SEVERE, ex.toString (), ex);
}
}
} catch (Exex ex) {log.log (Level.SEVERE, ex.toString (), ex);
} в конце концов {
if (ref! = null) {
ref.clear ();
}
}
}
if (log.isLoggable (Level.FINE)) {
log.log (Level.FINE, «завершение потока опросов {0} для {1}», новый объект [] {mycnt, this});
}
}
}
/ **
* Количество активных ссылок.
* /
закрытый финал AtomicInteger actCnt = new AtomicInteger ();
/ **
* Карта из T Ссылки на K объектов, которые будут использоваться для выпущенного (K) вызова
* /
private final ConcurrentHashMap, K> refmap = new ConcurrentHashMap, K> ();
/ **
* Добавляет отслеживаемую ссылку. dep ни в коем случае не должен ссылаться на ref
* Слабая ссылка. dep почти всегда является чем-то, на что ссылается ref.
* @throws IllegalArgumentException из ref и dep - это один и тот же объект.
* @param dep Зависимый объект, который нуждается в очистке, когда ref больше не ссылается.
* @param ref объект, ссылка на который должна отслеживаться
* /
public void trackReference (T ref, K dep) {
if (ref == dep) {
throw new IllegalArgumentException («Ссылочный объект и зависимый объект не могут быть одинаковыми»);
}
PhantomReference p = новый PhantomReference (ref, refqueue);
синхронизированный (refmap) {
refmap.put (p, dep);
if (actCnt.getAndIncrement () == 0 || running == false) {
if (actCnt.get ()> 0 && running == false) {
if (log.isLoggable (Level.FINE)) {
log.fine («запуск остановленной цепочки фантомного опроса»);
}
}
poll = new RefQueuePoll ();
poll.start ();
if (log.isLoggable (Level.FINE)) {
log.log (Level.FINE, «поток опроса # {0} создан для {1}», новый объект [] {tcnt.get (), this});
}
}
}
}
/ **
* Этот метод может быть вызван, если JVM, в которой находится трекер,
* завершение работы или другой контекст закрывается и объекты отслеживаются
* По трекеру теперь должен быть выпущен. Этот метод приведет к
* {@link #released (Object) release (K)} вызывается для каждой выдающейся ссылки.
* /
public void shutdown () {
Listrem;
// Копируем значения и очищаем карту, чтобы освободить
// вызывается только один раз, в противном случае GC позже удаляет ссылки
синхронизированный (refmap) {
rem = new ArrayList (refmap.values ());
refmap.clear ();
}
для (K dep: rem) {
пытаться {
освобожден (деп);
} catch (Exex ex) {
log.log (Level.SEVERE, ex.toString (), ex);
}
}
}
}