线程的ThreadLocals清洗

时间:2018-12-10 14:01:11

标签: java multithreading reflection concurrency thread-local

here我发现以下代码如何在Java中清除Thread的ThreadLocals:

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

这很复杂。遵循简单的代码,清理ThreadLocals就足够了吗?谢谢。

private void cleanThreadLocals(Thread thread) {
    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);
        threadLocalsField.set(thread, null);
    } catch (Exception e) {
        throw new RuntimeException(e);
    }
}

1 个答案:

答案 0 :(得分:0)

两个代码段都清洁ThreadLocals,但是它们有不同的方法。

较长的代码段首先从ThreadLocalMap获取所有线程ThreadLocals,然后对其调用remove()方法。您只能将其用于当前线程,因为remove()方法只是将当前线程中的remove()值,因此无法指定应该使用哪个线程。经过一些修改,您可以将其用于任何线程,必须直接在ThreadLocalMap上调用remove()。

较短的代码段从Thread中删除了整个ThreadLocalMap实例。它是延迟初始化的,因此将在需要时再次创建。您可以对任何线程使用此方法。

我测试了是否从JVM中删除了所有实例。该测试依赖于您可能需要在某些环境下调整GC行为的GC行为,但是在我的环境win10 / OracleJava8上,它开箱即用。

测试:

@Test
public void howToCleanThreadLocalValues() throws ReflectiveOperationException {
    Thread thread = Thread.currentThread();

    // Set thread local value for current thread
    WeakReference<ThreadLocal> threadLocal = new WeakReference<>(new ThreadLocal<>());
    threadLocal.get().set("foo");

    // Get ThreadLocalMap
    Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
    threadLocalsField.setAccessible(true);
    WeakReference<Object> threadLocalMap = new WeakReference<>(threadLocalsField.get(thread));
    Assert.assertNotNull(threadLocalMap.get());
    Assert.assertNotNull(threadLocal.get());

    // Set ThreadLocalMap to null, GC do the rest
    threadLocalsField.set(Thread.currentThread(), null);
    System.gc();
    Assert.assertNull(threadLocalMap.get());
    Assert.assertNull(threadLocal.get());
}