Очистка темы ThreadLocals - PullRequest
       20

Очистка темы ThreadLocals

0 голосов
/ 10 декабря 2018

здесь Я нашел следующий код для очистки ThreadLocals Thread в Java:

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 голосов
/ 12 декабря 2018

Оба фрагмента очищают ThreadLocals, но имеют другой подход.

Более длинный фрагмент сначала получает все ThreadLocals из ThreadLocalMap и вызывает метод remove () для них.Вы можете использовать его только для текущего потока, потому что метод remove () просто удаляет значение () из текущего потока, и нет способа указать, с каким потоком он должен работать.С некоторыми изменениями вы можете использовать его для любого потока, вам придется напрямую вызывать remove () в ThreadLocalMap.

Более короткий фрагмент удаляет весь экземпляр ThreadLocalMap из Thread.Он лениво инициализируется, поэтому при необходимости он будет создан заново.Вы можете использовать этот подход для любого потока.

Я проверил, все ли экземпляры удалены из JVM.Тест основан на поведении GC, вам, возможно, понадобится настроить поведение GC в какой-либо среде, но в моей среде win10 / OracleJava8 он проходит "из коробки".

Test:

@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());
}
...