如何清理 ThreadLocals

新手上路,请多包涵

有没有人有如何做到这一点的例子?它们是由垃圾收集器处理的吗?我正在使用 Tomcat 6。

原文由 Ricardo 发布,翻译遵循 CC BY-SA 4.0 许可协议

阅读 1.2k
2 个回答

javadoc 是这样说的:

“只要线程处于活动状态且 ThreadLocal 实例可访问,每个线程都会持有对其线程局部变量副本的隐式引用;线程消失后,其所有线程局部实例副本都将进行垃圾回收(除非存在对这些副本的其他引用)。

如果您的应用程序或(如果您正在谈论请求线程)容器使用线程池,这意味着线程不会死亡。如有必要,您需要自己处理线程局部变量。唯一干净的方法是调用 ThreadLocal.remove() 方法。

您可能希望为线程池中的线程清理线程局部变量的原因有两个:

  • 防止内存(或假设的资源)泄漏,或
  • 以防止信息通过线程本地从一个请求意外泄漏到另一个请求。

线程局部内存泄漏通常不应该是有界线程池的主要问题,因为任何线程局部内存最终都可能被覆盖;即当线程被重用时。但是,如果您错误地一遍又一遍地创建新的 ThreadLocal 实例(而不是使用 static 变量来保存单例实例),线程本地值将不会被覆盖,并将累积在每个线程的 threadlocals 映射中。这可能会导致严重泄漏。


假设您正在谈论在 webapp 处理 HTTP 请求期间创建/使用的线程局部变量,那么避免线程局部泄漏的一种方法是注册一个 ServletRequestListener 与您的 webapp 的 ServletContext 并实现侦听器的 requestDestroyed 方法来清除当前线程的线程局部变量。

请注意,在这种情况下,您还需要考虑 信息 从一个请求泄漏到另一个请求的可能性。

原文由 Stephen C 发布,翻译遵循 CC BY-SA 3.0 许可协议

当您没有对实际线程局部变量的引用时,这里有一些代码可以清除当前线程中的所有线程局部变量。您还可以将其概括为为其他线程清理线程局部变量:

     private void cleanThreadLocals() {
        try {
            // Get a reference to the thread locals table of the current thread
            Thread thread = Thread.currentThread();
            Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
            threadLocalsField.setAccessible(true);
            Object threadLocalTable = threadLocalsField.get(thread);

            // Get a reference to the array holding the thread local variables inside the
            // ThreadLocalMap of the current thread
            Class threadLocalMapClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
            Field tableField = threadLocalMapClass.getDeclaredField("table");
            tableField.setAccessible(true);
            Object table = tableField.get(threadLocalTable);

            // The key to the ThreadLocalMap is a WeakReference object. The referent field of this object
            // is a reference to the actual ThreadLocal variable
            Field referentField = Reference.class.getDeclaredField("referent");
            referentField.setAccessible(true);

            for (int i=0; i < Array.getLength(table); i++) {
                // Each entry in the table array of ThreadLocalMap is an Entry object
                // representing the thread local reference and its value
                Object entry = Array.get(table, i);
                if (entry != null) {
                    // Get a reference to the thread local object and remove it from the table
                    ThreadLocal threadLocal = (ThreadLocal)referentField.get(entry);
                    threadLocal.remove();
                }
            }
        } catch(Exception e) {
            // We will tolerate an exception here and just log it
            throw new IllegalStateException(e);
        }
    }

原文由 lyaffe 发布,翻译遵循 CC BY-SA 3.0 许可协议

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
推荐问题