使用线程本地数据库连接时,需要在线程存在时关闭连接。
只有当我可以覆盖调用线程的 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 垃圾收集的方法。