Treadlocal 学习(二)——Threading stories: ThreadLocal in web applications
This week I spend reasonable time to eliminate all our ThreadLocal variables in our web applications. The reason was that they created classloader leaks and we coudn't undeploy our applications properly anymore. Classloader leaks happen when a GC root keeps referencing an application object after the application was undeployed. If an application object is still referenced after undeploy, then the whole class loader can't be garbage collected cause the considered object references your applications class file which in turn references the classloader. This will cause an OutOfMemoryError after you've undeployed and redeployed a couple of times.
理解:由于Threadlocal变量引发了类加载器泄露,导致我们无法正确的地取消部署我们的应用。当一个垃圾回收器持续引用一个已经取消部署的应用对象,会导致类加载器泄露。如果一个应用对象在取消部署后仍然被引用,那么整个类加载器就无法被垃圾回收掉,从而导致对象持续引用你的类文件,类文件又引用你的类加载器(类加载器 -> 应用对象 -> 应用的类文件 -> 类加载器)从而导致了内存泄露(类加载器泄露),当你反复进行应用部署的时候。
ThreadLocal is one classic candidate that can easily create classloader leaks in web applications. The server is managing its threads in a pool. These threads live longer then your web application. In fact they don't die at all until the underlying JVM dies. Now, if you put a ThreadLocal in a pooled thread that references an object of your class you *must* be careful. You need to make sure that this variable is removed again using ThreadLocal.remove(). The issue in web applications is: where is the right place to safely remove ThreadLocal variables? Also, you may not want to modify that "removing code" every time a colleague decided to add another ThreadLocal to the managed threads. We've developed a wrapper class around thread local that keeps all the thread local variables in one single ThreadLocal variable. Here is the code.
理解:众所周知,在Web应用中,Threadlocal会引起类加器泄露。服务器用线程池来管理自己的线程。这些线程比web应用的生命周期长,直到虚拟机死亡后他们才会死亡。当你将一个指向类的对象的threadlocal变量放入一个线程池中的线程中,你必须要小心了,你必学保证这个变量被remove()函数清除掉。这个例子在web中的应用是这样子的:在什么地方清除threadlocal变量。为了避免在托管线程中每加一个threadlocal变量后你都要添加另相应的清除代码,我们写了一个wrapper类来将所有的threadlocal变量放入单一的threadlocal实例中。
public class ThreadLocalUtil { private final static ThreadLocal<ThreadVariables> THREAD_VARIABLES = new ThreadLocal<ThreadVariables>() { /** * @see java.lang.ThreadLocal#initialValue() */ @Override protected ThreadVariables initialValue() { return new ThreadVariables(); } }; public static Object getThreadVariable(String name) { return THREAD_VARIABLES.get().get(name); } public static Object getThreadVariable(String name, InitialValue initialValue) { Object o = THREAD_VARIABLES.get().get(name); if (o == null) { THREAD_VARIABLES.get().put(name, initialValue.create()); return getThreadVariable(name); } else { return o; } } public static void setThreadVariable(String name, Object value) { THREAD_VARIABLES.get().put(name, value); } public static void destroy() { THREAD_VARIABLES.remove(); } } public class ThreadVariables extends HashMap<String, Object> { } public abstract class InitialValue { public abstract Object create(); }
The advantage of the utility class is that no developer needs to manage the thread local variable lifecycle individually. The class puts all the thread locals in one map of variables. The destroy() method can be invoked where you can safely remove all thread locals in your web application. In our case thats aServletRequestListener -> requestDestroyed() method. You will also need to place finally blocks elsewhere. Typical places are near the HttpServlet, in the init(), doPost(), doGet() methods. This may remove all thread locals in the pooled worker threads after the request is done or an exception is thrown unexpectedly. Sometimes it happens that the main thread of the server leaks thread local variables. If that is the case you need to find the right places where to call the ThreadLocalUtil -> destroy() method. To do that figure out where the main thread actually *creates* the thread variables. You could use your debugger to do that.
Many guys out there suggest to ommit ThreadLocal in web applications for several reasons. It can be very difficult to remove them in a pooled thread environment so that you can undeploy the applications safely. ThreadLocal variables can be useful, but it's fair to consider other techniques before applying them. An alternative for web applications to carry request scope parameters is the HttpServletRequest. Many web frameworks allow for generic request parameter access as well as request/session attribute access, without ties to the native Servlet/Portlet API. Also many framework support request scoped beans to be injected into an object tree using dependency injection. All these options fulfill most requirements and should be considered prior to using ThreadLocal.
浙公网安备 33010602011771号