При использовании соединения с локальной базой данных потока закрытие соединения требуется, когда поток существует.
Это я могу сделать только в том случае, если смогу переопределить метод run() вызывающего потока. Даже это не лучшее решение, так как во время выхода я не знаю, открывалось ли когда-либо соединение этим потоком.
На самом деле проблема более общая: как заставить поток вызывать некоторый метод финализации локального объекта потока при его выходе.
Я просмотрел исходники java 1.5 и обнаружил, что локальная карта потока имеет значение null, что в конечном итоге приведет к тому, что сборщик мусора вызовет finalize(), но я не хочу рассчитывать на уборщик мусора.
Следующее переопределение кажется неизбежным, чтобы убедиться, что соединение с базой данных закрыто:
@Override
public void remove() {
get().release();
super.remove();
}
где release() закрывает соединение с базой данных, если оно было открыто. Но мы не знаем, использовал ли поток когда-либо этот локальный поток. Если get() никогда не вызывался этим потоком, то здесь будет напрасная трата усилий: будет вызвана функция ThreadLocal.initialValue(), в этом потоке будет создана карта и т. д.
Дальнейшее разъяснение и пример согласно комментарию Торбьерна:
java.lang.ThreadLocal — это тип фабрики для объекта, привязанного к потоку. Этот тип имеет геттер для объекта и фабричный метод (обычно пишется пользователем). Когда геттер вызывается, он вызывает фабричный метод только в том случае, если он никогда ранее не вызывался этим потоком.
Использование ThreadLocal позволяет разработчику привязать ресурс к потоку, даже если код потока был написан третьей стороной.
Пример. Допустим, у нас есть тип ресурса с именем MyType, и мы хотим иметь один и только один такой тип для каждого потока.
Определите в классе использования:
private static ThreadLocal<MyType> resourceFactory = new ThreadLocal<MyType>(){
@override
protected MyType initialValue(){
return new MyType();
}
}
Используйте в локальном контексте в этом классе:
public void someMethod(){
MyType resource = resourceFactory.get();
resource.useResource();
}
get() может вызывать initialValue() только один раз в жизненном цикле вызывающего потока. В этот момент экземпляр MyType создается и привязывается к этому потоку. Последующие вызовы get() этого потока снова ссылаются на этот объект.
Классический пример использования — когда MyType является небезопасным для потоков форматированием текста/даты/xml.
Но такие средства форматирования обычно не нужно освобождать или закрывать, в отличие от соединений с базой данных, и я использую java.lang.ThreadLocal, чтобы иметь одно соединение с базой данных для каждого потока.
На мой взгляд, java.lang.ThreadLocal почти идеально подходит для этого. Почти потому, что невозможно гарантировать закрытие ресурса, если вызывающий поток принадлежит стороннему приложению.
Мне нужны ваши мозги: расширив java.lang.ThreadLocal, мне удалось связать одно соединение с базой данных для каждого потока, для его исключительного использования, включая потоки, которые я не могу изменить или переопределить. Мне удалось убедиться, что соединения закрываются в случае, если поток умирает из-за неперехваченного исключения.
В случае нормального завершения потока сборщик мусора закрывает соединение (поскольку MyType переопределяет finalize()). На самом деле это происходит довольно быстро, но это не идеально.
Будь моя воля, в java.lang.ThreadLocal был бы другой метод:
protected void release() throws Throwable {}
Если бы этот метод существовал в java.lang.ThreadLocal, вызываемый JVM при выходе/смерти любого потока, то в моем собственном переопределении я мог бы закрыть свое соединение (и искупитель пришел бы в Сион) .
За неимением такого метода ищу другой способ подтвердить закрытие. Способ, который не будет полагаться на сборку мусора JVM.