ThreadLocal的误用可能会导致OutOfMemoryError
initialValue() {
return new SimpleDateFormat("MM/dd/yy");
}
};
public String formatCurrentDate() {
return df.get().format(new Date());
}
public String formatFirstOfJanyary1970() {
return df.get().format(new Date(0));
}
Going through a process like this, Tim has through painful lessons learned a powerful concept. Applied like in the last example, the result serves as a good example about the benefits.
可能的风险1:如果使用非JDK中的DateFormat,可能不是被bootstrap classloader加载;则使用之后还会存留在线程池中。而线程池中线程的生命周期会超过应用的生命周期,会阻止ClassLoader被垃圾回收,导致OutOfMemoryError。
But the newly-found concept is a dangerous one. If Tim had used one of the application classes instead of the JDK bundled DateFormat classes loaded by thebootstrap classloader, we are already in the danger zone. Just forgetting to remove it after the task at hand is completed, a copy of that Object will remain with the Thread, which tends to belong to athread pool. Since lifespan of the pooled Thread surpasses that of the application, it will prevent the object and thus aClassLoader being responsible for loading the application from being garbage collected. And we have created a leak, which has a chance to surface in a good oldjava.lang.OutOfMemoryError: PermGen space form
可能的风险2:把ThreadLocal用作获取全局上下文。
Another way to start abusing the concept is via using the ThreadLocal as a hack for getting a global context within your application. Going down thisrabbit hole is a sure way to mangle your application code with all kind of unimaginary dependencies coupling your whole code base into an unmaintainable mess.
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.
——如果在应用被undeployed之后,GC root还保持着某个应用对象的引用;→ 则整个classloader都不会被垃圾回收;→ 则会导致ClassLoader Leak。导致OutOfMemoryError
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 than 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 usingThreadLocal.remove()
.
——服务器总是把Thread放到线程池中,而线程池会被你的应用程序活得更久。所以在使用ThreadLocal时要小心,你需要调用ThreadLocal.remove()来删除ThreadLocal变量。
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 anotherThreadLocal
to the managed threads.
We’ve developed a wrapper class around thread local that keeps all the thread local variables in one singleThreadLocal
variable. Here is the code.
——那么应当何时调用ThreadLocal.remove()呢?
public class ThreadLocalUtil {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 theHttpServlet
, 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 themain
thread of the server leaks thread local variables. If that is the case you need to find the right places where to call theThreadLocalUtil -> destroy()
method. To do that figure out where the main thread actually *creates* the thread variables. You could use your debugger to do that.
——这个工具类将所有ThreadLocal变量放到一个map中,不需要程序员独自管理ThreadLocal变量的生命周期。destroy()能够移除所有ThreadLocal变量,可以在ServletRequestListener.requestDestroyed()中调用它。
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 theHttpServletRequest
. 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 usingThreadLocal
.