Joe*_*tov 13 java thread-local resource-disposal
使用线程本地数据库连接时,线程存在时需要关闭连接.
这只能我可以覆盖调用线程的run()方法.即使这不是一个很好的解决方案,因为在退出时,我不知道该线程是否曾打开过连接.
问题实际上更为通用:如何强制线程在线程局部对象退出时调用某些终结方法.
我查看了java 1.5的源代码,发现线程本地映射设置为null,这最终会导致垃圾收集调用finalize(),但我不想指望垃圾收集器.
以下覆盖似乎是不可避免的,以确保关闭数据库连接:
@Override
public void remove() {
get().release();
super.remove();
}
Run Code Online (Sandbox Code Playgroud)
其中,release()关闭数据库连接(如果已打开).但我们不知道线程是否曾使用过本地线程.如果此线程从未调用过get(),那么这里的工作就相当浪费:将调用ThreadLocal.initialValue(),在此线程上创建一个map等.
-
根据Thorbjørn的评论进一步澄清和举例:
java.lang.ThreadLocal是绑定到线程的对象的工厂类型.此类型具有对象的getter和工厂方法(通常由用户编写).当调用getter时,只有在此线程之前从未调用过它时才调用工厂方法.
使用ThreadLocal允许开发人员将资源绑定到线程,即使线程代码是由第三方编写的.
示例:假设我们有一个名为MyType的资源类型,我们希望每个线程只有一个.
在using类中定义:private static ThreadLocal resourceFactory = new ThreadLocal(){@ override protected MyType initialValue(){return new MyType(); }}
在此类的本地上下文中使用:public void someMethod(){MyType resource = resourceFactory.get(); resource.useResource(); }
get()只能在调用线程的生命周期中调用initialValue()一次.此时,MyType的实例将被实例化并绑定到此线程.此线程对get()的后续调用再次引用此对象.
经典用法示例是MyType是一些线程不安全的text/date/xml格式化程序.
但是这样的格式化程序通常不需要被释放或关闭,数据库连接也是如此,我使用java.lang.ThreadLocal来为每个线程建立一个数据库连接.
我看待它的方式,java.lang.ThreadLocal几乎是完美的.几乎是因为如果调用线程属于第三方应用程序,则无法保证资源的关闭.
我需要你的大脑绅士:通过扩展java.lang.ThreadLocal,我设法为每个线程绑定一个数据库连接,因为它的独占用途 - 包括我无法修改或覆盖的线程.我设法确保在线程因未捕获的异常而死的情况下关闭连接.
在正常线程退出的情况下,垃圾收集器关闭连接(因为MyType会覆盖finalize()).实际上它很快发生,但这并不理想.
如果我按照自己的方式,java.lang.ThreadLocal上会有另一种方法:
protected void release() throws Throwable {}
Run Code Online (Sandbox Code Playgroud)
如果这个方法存在于java.lang.ThreadLocal上,由JVM在任何线程退出/死亡时调用,那么在我自己的覆盖中,我可以关闭我的连接(并且救赎者将来到Zion).
在没有这种方法的情况下,我正在寻找另一种方法来确认关闭.一种不依赖于JVM垃圾收集的方法.
谢谢
McD*_*ell 13
如果你是一个敏感的性格,现在就把目光移开.
我不希望这种扩展得很好; 它有效地使系统中的线程数加倍.可能有一些用例可以接受.
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();
}
}
Run Code Online (Sandbox Code Playgroud)
更好的错误处理等等留给了实施者的练习.
您还可以查看跟踪ConcurrentHashMap另一个线程轮询isAlive的线程/资源.但是这种解决方案是绝望的最后手段 - 对象可能最终会经常被检查或者很少被检查.
我想不出任何其他不涉及仪器的东西.AOP可能会奏效.
连接池将是我最喜欢的选项.
用一个新的Runnable包装你的Runnable
try {
wrappedRunnable.run();
} finally {
doMandatoryStuff();
}
Run Code Online (Sandbox Code Playgroud)
建设,让我们执行.
你甚至可以把它变成一个方法,例如:
Runnable closingRunnable(Runnable wrappedRunnable) {
return new Runnable() {
@Override
public void run() {
try {
wrappedRunnable.run();
} finally {
doMandatoryStuff();
}
}
};
}
Run Code Online (Sandbox Code Playgroud)
并调用该方法传入您正在寻找的runnable.
您可能还想考虑使用Executor.使管理Runable和Callables变得更加容易.
如果你使用ExecutorService,你可以使用它 executor.submit(closingRunnable(normalRunnable))
如果您知道您将关闭整个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;
}
Run Code Online (Sandbox Code Playgroud)
关于doMandatoryStuff是否可以知道以前是否曾经打开过连接,我想到的一件事就是让第二个ThreadLocal跟踪它是否被打开(例如:连接打开时,获取然后在清理时将AtomicInteger设置为2,检查它是否仍然是默认值,比如1 ...)
| 归档时间: |
|
| 查看次数: |
8800 次 |
| 最近记录: |