一种可能的(和常见的)用法是当你有一些不是线程安全的对象,但是你想避免同步对该对象的访问(我正在看你, 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);
}
}
文档 。
由于ThreadLocal
是对给定Thread
内数据的引用,因此在使用线程池的应用程序服务器中使用ThreadLocal
时,最终可能会出现类加载泄漏。你需要非常小心,通过使用ThreadLocal
的remove()
方法清理你get()
任何ThreadLocal
get()
或set()
。
如果在完成后没有清理,那么它作为已部署的 webapp 的一部分加载的类所持有的任何引用都将保留在永久堆中,并且永远不会被垃圾收集。重新部署 / 取消部署 webapp 不会清除每个Thread
对 webapp 类的引用,因为Thread
不是您的 webapp 拥有的东西。每个连续的部署都将创建一个永远不会被垃圾收集的类的新实例。
由于java.lang.OutOfMemoryError: PermGen space
并且在一些谷歌搜索之后可能只会增加-XX:MaxPermSize
而不是修复错误,最终将导致内存不足。
如果您最终遇到这些问题,可以使用Eclipse 的 Memory Analyzer和 / 或遵循Frank Kieviet 的指南和后续内容来确定哪个线程和类保留了这些引用。
更新:重新发现了Alex Vasseur 的博客文章 ,帮助我找到了我遇到的一些ThreadLocal
问题。
许多框架使用 ThreadLocals 来维护与当前线程相关的一些上下文。例如,当当前事务存储在 ThreadLocal 中时,您不需要通过每个方法调用将其作为参数传递,以防有人在堆栈中需要访问它。 Web 应用程序可能会在 ThreadLocal 中存储有关当前请求和会话的信息,以便应用程序可以轻松访问它们。使用 Guice,您可以在为注入的对象实现自定义作用域时使用 ThreadLocals(Guice 的默认servlet 作用域也很可能也使用它们)。
ThreadLocals 是一种全局变量(尽管它们仅限于一个线程,因为它们仅限于一个线程),所以在使用它们时应该小心,以避免不必要的副作用和内存泄漏。设计您的 API,以便在不再需要 ThreadLocal 值时将始终自动清除它们,并且无法正确使用 API(例如, 像这样 )。 ThreadLocals 可以用来使代码更清洁,在某些罕见的情况下,他们是做什么工作的唯一方法(我当前的项目有两个这样的情况下,它们都记录在这里下的 “静态字段和全局变量”)。