简体   繁体   English

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

[英]How to force a Java thread to close a thread-local database connection

When Using a thread-local database connection, closure of the connection is required when the thread exists.当使用线程本地数据库连接时,当线程存在时需要关闭连接。

This I can only do if I can override the run() method of the calling thread.只有当我可以覆盖调用线程的 run() 方法时,我才能做到这一点。 Even that is not a great solution, since at time of exit, I don't know if a connection has ever been opened by that thread.即使这不是一个很好的解决方案,因为在退出时,我不知道该线程是否曾经打开过连接。

The problem is in fact more general: How to force a thread to call some finalisation method of a thread-local object when it exits.问题实际上更普遍:如何强制线程在退出时调用线程本地对象的某些终结方法。

I looked at the sources of java 1.5 and found that the thread local map is set to null, which will eventually cause the garbage collection to call finalize() , but I don't want to count on the garbage collector.我查看了java 1.5的源码,发现线程本地映射设置为null,最终会导致垃圾收集调用finalize() ,但是我不想指望垃圾收集器。

The following override seems inevitable to make sure that a database connection is closed:为了确保关闭数据库连接,以下覆盖似乎是不可避免的:

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

where release() closes the database connection, if it has been opened.其中release()关闭数据库连接,如果它已经打开。 But we don't know if the thread has ever used this thread-local.但是我们不知道线程是否曾经使用过这个线程本地。 If get() has never been called by this thread, then there's quite a waste of effort here: ThreadLocal.initialValue() will be called, a map will be created on this thread, etc.如果 get() 从来没有被这个线程调用过,那么这里就很浪费精力: ThreadLocal.initialValue()将被调用,一个映射将在这个线程上创建,等等。


Further clarification and example as per Thorbjørn's comment:根据 Thorbjørn 的评论进一步说明和示例:

java.lang.ThreadLocal is a type of factory for an object that is bound to a thread. java.lang.ThreadLocal是绑定到线程的对象的一种工厂类型。 This type has a getter for the object and a factory method (typically written by the user).此类型具有对象的 getter 和工厂方法(通常由用户编写)。 When the getter is called it calls the factory method only if it has never been called before by this thread.当 getter 被调用时,只有在此线程之前从未调用过它时,它才会调用工厂方法。

Using ThreadLocal allows a developer to bind a resource to a thread even if the thread code was written by a third party.使用ThreadLocal允许开发人员将资源绑定到线程,即使线程代码是由第三方编写的。

Example: Say we have a resource Type called MyType and we want to have one and only one of it per thread.示例:假设我们有一个名为MyType的资源类型,我们希望每个线程只有一个。

Define in the using class:在 using 类中定义:

private static ThreadLocal<MyType> resourceFactory = new ThreadLocal<MyType>(){
    @override
    protected MyType initialValue(){
        return new MyType();
    }
}

Use in local context in this class:在此类的本地上下文中使用:

public void someMethod(){
    MyType resource = resourceFactory.get();
    resource.useResource();
}

get() can call initialValue() only once in the life cycle of the calling thread. get()只能在调用线程的生命周期中调用initialValue()一次。 At that point an instance of MyType gets instantiated and bound to this thread.此时MyType 的一个实例被实例化并绑定到这个线程。 Subsequent calls to get() by this thread refer again to this object.此线程对get()的后续调用再次引用此对象。

The classic usage example is when MyType is some thread-unsafe text/date/xml formatter.经典用法示例是MyType是一些线程不安全的文本/日期/xml 格式化程序。

But such formatters usually don't need to be released or closed, database connections do and I am using java.lang.ThreadLocal to have a one database connection per thread.但是这样的格式化程序通常不需要释放或关闭,数据库连接需要,我使用java.lang.ThreadLocal每个线程有一个数据库连接。

The way I see it, java.lang.ThreadLocal is almost perfect for that.在我看来, java.lang.ThreadLocal几乎是完美的。 Almost because there's no way to guarantee closure of the resource if the calling thread belongs to a third party application.几乎是因为如果调用线程属于第三方应用程序,则无法保证资源关闭。

I need your brains squires: By extending java.lang.ThreadLocal I managed to bind one database connection for every thread, for it's exclusive usage - including threads that I can not modify or override.我需要你的大脑乡绅:通过扩展java.lang.ThreadLocal我设法为每个线程绑定一个数据库连接,因为它是独占使用 - 包括我无法修改或覆盖的线程。 I managed to make sure that the connections get closed in case the thread dies on uncaught exception.我设法确保连接关闭,以防线程因未捕获的异常而死亡。

In case of normal thread exit, the garbage collector closes the connection (because MyType overrides the finalize() ).在正常线程退出的情况下,垃圾收集器关闭连接(因为MyType覆盖了finalize() )。 In actual fact it happens quite quickly, but this is not ideal.事实上,它发生得很快,但这并不理想。

If I had my way, there would have been another method on the java.lang.ThreadLocal :如果我有我的方式, java.lang.ThreadLocal上会有另一种方法:

protected void release() throws Throwable {}

If this method existed on java.lang.ThreadLocal , called by JVM upon any thread exit/death, then in my own override of it I could close my connection (and the redeemer would have come to Zion).如果这个方法存在于java.lang.ThreadLocal 上,在任何线程退出/死亡时由 JVM 调用,那么在我自己的覆盖中,我可以关闭我的连接(并且救赎者会来到锡安)。

In the absence of such method, I'm looking for another way to confirm closure.在没有这种方法的情况下,我正在寻找另一种方法来确认关闭。 A way that won't rely on the JVM garbage collection.一种不依赖于 JVM 垃圾收集的方法。

If you are of a sensitive disposition, look away now.如果你是一个敏感的性格,现在把目光移开。

I wouldn't expect this to scale very well;我不希望这能很好地扩展; it effectively doubles the number of threads in the system.它有效地使系统中的线程数增加了一倍。 There may be some use cases where it is acceptable.可能有一些用例是可以接受的。

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();
  }
}

Better error handling and so on is left as an exercise for the implementer.更好的错误处理等留给实现者作为练习。

You could also have a look at tracking the threads/resources in a ConcurrentHashMap with another thread polling isAlive .您还可以查看使用另一个线程轮询isAlive来跟踪ConcurrentHashMap的线程/资源。 But that solution is the last resort of the desperate - objects will probably end up being checked too often or too seldom.但该解决方案是绝望的最后手段 - 对象最终可能会被检查得太频繁或太少。

I can't think of anything else that doesn't involve instrumentation.我想不出任何不涉及仪器的东西。 AOP might work. AOP 可能会起作用。

Connection pooling would be my favoured option.连接池将是我最喜欢的选择。

Wrap your Runnable with a new Runnable with a用一个新的 Runnable 包裹你的 Runnable

try {
  wrappedRunnable.run();
} finally {
  doMandatoryStuff();
}

construction, and let THAT be executed instead.构造,并让 THAT 改为执行。

You could even make it into a method, ex:你甚至可以把它变成一个方法,例如:

  Runnable closingRunnable(Runnable wrappedRunnable) {
    return new Runnable() {
      @Override
      public void run() {
        try {
          wrappedRunnable.run();
        } finally {
          doMandatoryStuff();
        }
      }
    };
  }

and call that method passing in the runnable you're looking for.并调用该方法传入您正在寻找的可运行对象。

You may also want to consider using an Executor instead.您可能还想考虑使用 Executor 代替。 Makes it much easier to manage Runable and Callables.使管理 Runable 和 Callables 变得更加容易。

If you do use an ExecutorService, you can use it like executor.submit(closingRunnable(normalRunnable))如果您确实使用了 ExecutorService,则可以像executor.submit(closingRunnable(normalRunnable))一样使用它

If you know that you'll be shutting down your entire ExecutorService and want connections to close at that point, you can set a thread factory which also does the close "after all tasks are done and shutdown called on the executor", example:如果您知道您将关闭整个 ExecutorService 并希望在那时关闭连接,您可以设置一个线程工厂,它也关闭“在所有任务完成并在执行程序上调用关闭之后”,例如:

  ExecutorService autoClosingThreadPool(int numThreads) {
    ThreadPoolExecutor threadPool = new ThreadPoolExecutor(numThreads, numThreads, 0L, TimeUnit.MILLISECONDS, new LinkedBlockingQueue<Runnable>()); // same as Executors.newFixedThreadPool
    threadPool.setThreadFactory(new ThreadFactory() {
      @Override
      public Thread newThread(Runnable r) {
        return new Thread(closingRunnable(r)); // closes it when executor is shutdown
      }
    });
    return threadPool;
  }

In terms of whether or not doMandatoryStuff can know whether or not the connection was ever opened or not previously, one thing that comes to mind is having a second ThreadLocal that just tracks if it was opened or not (ex: when connection is opened, get then set an AtomicInteger to 2, at cleanup time, check to see if it's still at its default, say, 1...)就 doMandatoryStuff 是否可以知道连接以前是否打开过而言,想到的一件事是有第二个 ThreadLocal 只跟踪它是否打开(例如:当连接打开时,获取然后在清理时将 AtomicInteger 设置为 2,检查它是否仍处于默认状态,例如 1...)

The normal JDBC practice is that you close the Connection (and Statement and ResultSet ) in the very same method block as you'd acquired it.正常的 JDBC 实践是在与获取它相同的方法块中关闭Connection (以及StatementResultSet )。

In code:在代码中:

Connection connection = null;

try {
    connection = getConnectionSomehow();
    // Do stuff.
} finally {
    if (connection != null) {
        try {
            connection.close();
        } catch (SQLException e) {
            ignoreOrLogItButDontThrowIt(e);
        }
    }
}

Keeping this in mind, your question makes me think that there's something wrong in your design.记住这一点,你的问题让我觉得你的设计有问题。 Acquiring and closing those kind of expensive and external resources in the shortest scope will save the application from potential resource leaks and crashes.在最短的范围内获取和关闭这些昂贵的外部资源将使应用程序免于潜在的资源泄漏和崩溃。

If your original intent was to improve connecting performance, then you need to take a look for connection pooling .如果您的初衷是提高连接性能,那么您需要查看连接池 You can use for example the C3P0 API for this.例如,您可以为此使用C3P0 API。 Or if it's a webapplication, use the appserver's builtin connection pooling facilities in flavor of a DataSource .或者,如果它是一个 Web 应用程序,请使用应用程序服务器的内置连接池设施,具有DataSource风格。 Consult the appserver specific documentation for details.有关详细信息,请参阅特定于应用程序服务器的文档。

I don't really understand why you are not using traditional connection pooling.我真的不明白你为什么不使用传统的连接池。 But I shall assume you have your reasons.但我假设你有你的理由。

How much freedom do you have?你有多少自由? As some DI frameworks do support object life cycles and thread-scoped variables (all nicely proxied).由于某些 DI 框架确实支持对象生命周期和线程范围的变量(所有这些都很好地代理)。 Could you use one of them?你能用其中之一吗? I think Spring would do this all out of the box, while Guice would need a 3rd party lib to handle life cycles and thread scopes.我认为 Spring 会开箱即用,而 Guice 需要一个 3rd 方库来处理生命周期和线程范围。

Next how much control do you have on either the creating of the ThreadLocal variable or the creation of threads?接下来,您对 ThreadLocal 变量的创建或线程的创建有多少控制权? I am guessing you have complete control on the ThreadLocal but limited to none on the creation of threads?我猜你对 ThreadLocal 有完全的控制权,但对线程的创建没有限制?

Could you use Aspect-oriented programming to monitor for new Runnable or Threads extending the run() method to include the clean up?您能否使用面向方面的编程来监视新的 Runnable 或扩展 run() 方法以包括清理的线程? You will also need to extend the ThreadLocal so it can register itself.您还需要扩展 ThreadLocal 以便它可以自行注册。

You had to open the connection once, so you also have to handle the closure at the same place.您必须打开连接一次,因此您还必须在同一位置处理关闭。 Depending on your environment the threads may be reused and you cannot expect the thread to be garbage collected before the shutdown of the application.根据您的环境,线程可能会被重用,并且您不能期望在应用程序关闭之前对线程进行垃圾回收。

I think in the general case there is no good solution for this, other than the classic: Code that gets the resource has to be responsible for closing it.我认为在一般情况下,除了经典之外,没有好的解决方案:获取资源的代码必须负责关闭它。

In the specific case, if you are calling threads to this degree you could pass in the connection to your methods at the start of the thread, either with a method that takes a custom parameter or via some form of Dependency Injection.在特定情况下,如果您以这种程度调用线程,您可以在线程开始时将连接传递到您的方法,使用带有自定义参数的方法或通过某种形式的依赖注入。 Then since you have the code that supplies the connection, you have the code that removes it.然后,由于您拥有提供连接的代码,因此您拥有删除它的代码。

A Dependency Injection based on annotations might work here, as code that doesn't require the connection won't get one and therefore wouldn't need to be closed, but it sounds like your design is too far along to retrofit something like that.基于注解的依赖注入可能在这里工作,因为不需要连接的代码不会得到连接,因此不需要关闭,但听起来你的设计太远了,无法改造这样的东西。

Override the get() method in ThreaedLocal so that it sets a List property on the subclass.覆盖 ThraedLocal 中的 get() 方法,以便它在子类上设置 List 属性。 This property can easily be queried to determine if the get() method had been called for a particular thread.可以轻松查询此属性以确定是否为特定线程调用了 get() 方法。 You could then access the ThreadLocal to clean it up in that case.在这种情况下,您可以访问 ThreadLocal 来清理它。

Updated in response to comment更新以回应评论

What we did was我们所做的是

@Override
public void run() {
  try {
    // ...
  } finally {
    resource.close();
  }
}

Basically just always (possibly open and then) close it for all paths through the thread.基本上总是(可能打开然后)关闭它通过线程的所有路径。 In case it helps anybody out there :)如果它可以帮助那里的任何人:)

I'm looking at the same problem.我在看同样的问题。 Looks like so far you have to use finalize(), though it is now deprecated.看起来到目前为止您必须使用 finalize(),尽管它现在已被弃用。 As the tasks are submitted to some Executor, you'd never know when a thread exactly exits, unless the Executor shows you, that means you have control of the Executor to some extend.当任务提交给某个 Executor 时,您永远不会知道线程何时确切退出,除非 Executor 向您显示,这意味着您在某种程度上可以控制 Executor。

For example, if you build the Executor by extending ThreadPoolExecutor, you may override the afterExecution() and the terminated() methods to accomplish it.例如,如果您通过扩展 ThreadPoolExecutor 来构建 Executor,您可以覆盖 afterExecution() 和 terminate() 方法来完成它。 The former for a thread exiting abnormally while the latter for normally.前者为线程异常退出,后者为正常退出。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM