Thread的ThreadLocals清理
Thread's ThreadLocals cleaning
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);
}
}
两个片段都清除了 ThreadLocals,但它们有不同的方法。
较长的代码段首先从 ThreadLocalMap 获取所有线程 ThreadLocals 并对其调用 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());
}
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);
}
}
两个片段都清除了 ThreadLocals,但它们有不同的方法。
较长的代码段首先从 ThreadLocalMap 获取所有线程 ThreadLocals 并对其调用 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());
}