java - 如何强制 Java 线程关闭线程本地数据库连接

标签 java thread-local resource-disposal

当使用线程本地数据库连接时,当线程存在时需要关闭连接。

只有当我可以覆盖调用线程的 run() 方法时,我才能这样做。即使这不是一个很好的解决方案,因为在退出时,我不知道该线程是否曾经打开过连接。

问题实际上更普遍:如何强制线程在退出时调用线程局部对象的某些终结方法。

看了java 1.5的源码,发现thread local map设置为null,最终会导致垃圾回收调用finalize(),但我不想依靠垃圾收集器。

为了确保关闭数据库连接,以下重写似乎是不可避免的:

@Override 
public void remove() {
    get().release(); 
    super.remove(); 
}

其中 release() 关闭数据库连接(如果已打开)。但是我们不知道线程是否曾经使用过这个thread-local。如果 get() 从未被该线程调用过,那么这里就相当浪费精力了:ThreadLocal.initialValue() 将被调用,一个映射将在该线程上创建,等等。


根据 Thorbjørn 的评论进一步说明和示例:

java.lang.ThreadLocal 是一种绑定(bind)到线程的对象工厂。这种类型有一个对象的 getter 和一个工厂方法(通常由用户编写)。调用 getter 时,只有在该线程之前从未调用过它时,它才会调用工厂方法。

使用 ThreadLocal 允许开发人员将资源绑定(bind)到线程,即使线程代码是由第三方编写的。

例子: 假设我们有一个名为 MyType 的资源类型,我们希望每个线程只有一个。

在using类中定义:

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 的一个实例被实例化并绑定(bind)到这个线程。此线程对 get() 的后续调用再次引用此对象。

经典的用法示例是 MyType 是一些线程不安全的文本/日期/xml 格式化程序。

但是这样的格式化程序通常不需要释放或关闭,数据库连接需要,我正在使用 java.lang.ThreadLocal 每个线程有一个数据库连接。

在我看来,java.lang.ThreadLocal 几乎是完美的。几乎是因为如果调用线程属于第三方应用程序,则无法保证资源的关闭。

我需要你的头脑 squires:通过扩展 java.lang.ThreadLocal,我设法为每个线程绑定(bind)一个数据库连接,因为它是独占使用 - 包括我无法修改或覆盖的线程。我设法确保关闭连接,以防线程因未捕获的异常而死亡。

在线程正常退出的情况下,垃圾收集器会关闭连接(因为 MyType 覆盖了 finalize())。实际上它发生得很快,但这并不理想。

如果我有办法,java.lang.ThreadLocal 上会有另一种方法:

protected void release() throws Throwable {}

如果此方法存在于 java.lang.ThreadLocal 上,由 JVM 在任何线程退出/死亡时调用,那么在我自己的覆盖中我可以关闭我的连接(救赎者会来到锡安)。

在没有这种方法的情况下,我正在寻找另一种方法来确认关闭。一种不依赖于 JVM 垃圾收集的方法。

最佳答案

如果你是一个敏感的人,现在就把目光移开。

我不希望它扩展得很好;它有效地使系统中的线程数量增加了一倍。可能存在一些可以接受的用例。

public class Estragon {
  public static class Vladimir {
    Vladimir() { System.out.println("Open"); }
    public void close() { System.out.println("Close");}
  }

  private static ThreadLocal<Vladimir> HOLDER = new ThreadLocal<Vladimir>() {
    @Override protected Vladimir initialValue() {
      return createResource();
    }
  };

  private static Vladimir createResource() {
    final Vladimir resource = new Vladimir();
    final Thread godot = Thread.currentThread();
    new Thread() {
      @Override public void run() {
        try {
          godot.join();
        } catch (InterruptedException e) {
          // thread dying; ignore
        } finally {
          resource.close();
        }
      }
    }.start();
    return resource;
  }

  public static Vladimir getResource() {
    return HOLDER.get();
  }
}

更好的错误处理等留给实现者练习。

您还可以查看使用另一个线程轮询跟踪 ConcurrentHashMap 中的线程/资源 isAlive .但该解决方案是孤注一掷的最后手段 - 对象最终可能会被检查得太频繁或太少。

我想不出还有什么不涉及仪器的。 AOP 可能会起作用。

连接池是我的首选。

关于java - 如何强制 Java 线程关闭线程本地数据库连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/1700212/

相关文章:

正常关闭时 Tomcat 内存泄漏警告 - Codahale

java - servlet 中的线程局部变量

Jenkins 异步资源处理器问题

java - Dagger 2 - 无法初始化

java - 使用 IN 子句过滤 Spark Cassandra 连接器

java - 将包含多个文件的Inputstream放入一个ZipEntry中

java - 如何从 Java 中的两个对象中检索值?

java - 为什么有些网络服务器会提示他们造成的内存泄漏?