协慌网

登录 贡献 社区

何时以及如何使用 ThreadLocal 变量?

我什么时候应该使用ThreadLocal变量?

怎么用?

答案

一种可能的(和常见的)用法是当你有一些不是线程安全的对象,但是你想避免同步对该对象的访问(我正在看你, 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时,最终可能会出现类加载泄漏。你需要非常小心,通过使用ThreadLocalremove()方法清理你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 可以用来使代码更清洁,在某些罕见的情况下,他们是做什么工作的唯一方法(我当前的项目有两个这样的情况下,它们都记录在这里下的 “静态字段和全局变量”)。