当使用线程本地数据库连接时,需要在线程存在时关闭连接。
只有当我能覆盖调用线程的run()方法时,才能做到这一点。即使如此,这也不是一个很好的解决方案,因为在退出时,我不知道该线程是否曾经打开过连接。
问题实际上更普遍:如何强制线程在退出时调用线程本地对象的某些终止方法。
我查看了Java 1.5的源代码,并发现线程本地映射被设置为null,这最终会导致垃圾收集器调用finalize(),但我不想依赖于垃圾收集器。
为确保关闭数据库连接,必须进行以下重写:
@Override
public void remove() {
get().release();
super.remove();
}
在这里,release() 方法关闭数据库连接,如果它已经被打开。但我们不知道该线程是否曾经使用过这个线程本地变量。如果该线程从未调用过 get()
方法,则会浪费很多资源:将调用 ThreadLocal.initialValue() 方法,在此线程上创建一个映射表等。
根据 Thorbjørn 的评论进一步解释和示例:
java.lang.ThreadLocal 是一种绑定到线程的对象工厂。这种类型有一个获取器(getter)和一个工厂方法(通常由用户编写)。当 getter 被调用时,仅在该线程之前从未调用过它时才调用工厂方法。
使用 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垃圾收集的方式。