问题
我什么时候应该使用ThreadLocal
变量?它应该如何使用?
#1 热门回答(771 赞)
一种常见的用法是当你有一些不是线程安全的对象时,你想避免对该对象的并发访问(如:SimpleDateFormat),而是给每个线程自己的对象实例。
例如:
public class Foo
{
// SimpleDateFormat is not thread-safe, so give one to each thread
private static final ThreadLocal<SimpleDateFormat> formatter = new ThreadLocal<SimpleDateFormat>(){
@Override
protected SimpleDateFormat initialValue()
{
return new SimpleDateFormat("yyyyMMdd HHmm");
}
};
public String formatIt(Date date)
{
return formatter.get().format(date);
}
}
#2 热门回答(377 赞)
由于ThreadLocal
是给定Thread
中的数据的引用,因此在使用线程池的应用程序服务器中使用ThreadLocal
时,最终可能导致类加载泄漏。通过使用 ThreadLocal
的 remove()
方法,你需要非常小心地清理你 get() 或 set()的任何 ThreadLocal
。
如果在完成时没有清理,那么对于作为已部署Web应用程序的一部分加载的类的引用将保留在permanent heap中,并且永远不会收集垃圾。重新部署/取消部署 webapp 不会清理每个Thread
对你的 webapp 类的引用,因为Thread
不是你的 webapp 所拥有的。每个连续的部署将创建一个不会被垃圾收集的类的新实例。
由于java.lang.OutOfMemoryError:PermGen space
,最终导致内存不足,并且在一些 googling 之后可能只会增加-XX:MaxPermSize
而不是修复bug。
如果您最终遇到这些问题,则可以使用Eclipse's Memory Analyzer或跟随Frank Kieviet's guide和followup来确定哪个线程和类保留了这些引用。
更新:
重新发现Alex Vasseur's blog entry,帮助我追踪我遇到的一些ThreadLocal
问题。
#3 热门回答(122 赞)
许多框架使用 ThreadLocals 来维护与当前线程相关的一些上下文。例如,当当前事务存储在ThreadLocal中时,您无需通过每个方法调用将其作为参数传递,以防堆栈中的某人需要访问它。 Web应用程序可能会将有关当前请求和会话的信息存储在ThreadLocal中,以便应用程序可以轻松访问它们。使用Guice时,可以在为注入对象实现custom scopes时使用ThreadLocals(Guice的defaultservlet scopes最可能也使用它们)。
ThreadLocals是一种全局变量(尽管稍微不那么邪恶,因为它们仅限于一个线程),所以在使用它们以避免不必要的副作用和内存泄漏时应该小心。设计你的 API,以便在不再需要 ThreadLocal 值时,将始终自动清除ThreadLocal值,并且不可能错误地使用API(例如like this)。 ThreadLocals可以用来使代码更清洁,在某些罕见的情况下,他们是做什么工作的唯一方法(我当前的项目有两个这样的情况下,他们是documented here under “静态字段和全局变量”)。