如何清理线程局部

2022-08-31 13:49:20

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


答案 1

javadoc 是这样说的:

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

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

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

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

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


假设您正在谈论在 Web 应用处理 HTTP 请求期间创建/使用的线程局部变量,那么避免线程本地泄漏的一种方法是向 webapp 注册 a 并实现侦听器的方法以清理当前线程的线程局部变量。ServletRequestListenerServletContextrequestDestroyed

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


答案 2

下面是一些代码,用于在没有对实际线程局部变量的引用时从当前线程中清除所有线程局部变量。您还可以将其泛化以清理其他线程的线程局部变量:

    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);
        }
    }

推荐