package net.csdn.common.thread; import net.csdn.common.logging.CSLogger; import net.csdn.common.logging.Loggers; import java.lang.ref.Reference; import java.lang.reflect.Field; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; /** * BlogInfo: william * Date: 11-9-5 * Time: 下午10:20 */ public class ThreadLocals { private static final CSLogger logger = Loggers.getLogger(ThreadLocals.class); public static class CleanableValue<T> { private T value; public CleanableValue(T value) { this.value = value; } public T get() { return value; } public void set(T value) { this.value = value; } } public static void clearReferencesThreadLocals() { try { Thread[] threads = getThreads(); // Make the fields in the Thread class that store ThreadLocals // accessible Field threadLocalsField = Thread.class.getDeclaredField("threadLocals"); threadLocalsField.setAccessible(true); Field inheritableThreadLocalsField = Thread.class.getDeclaredField("inheritableThreadLocals"); inheritableThreadLocalsField.setAccessible(true); // Make the underlying array of ThreadLoad.ThreadLocalMap.Entry objects // accessible Class<?> tlmClass = Class.forName("java.lang.ThreadLocal$ThreadLocalMap"); Field tableField = tlmClass.getDeclaredField("table"); tableField.setAccessible(true); for (int i = 0; i < threads.length; i++) { Object threadLocalMap; if (threads[i] != null) { // Clear the first map threadLocalMap = threadLocalsField.get(threads[i]); clearThreadLocalMap(threadLocalMap, tableField); // Clear the second map threadLocalMap = inheritableThreadLocalsField.get(threads[i]); clearThreadLocalMap(threadLocalMap, tableField); } } } catch (Exception e) { logger.warn("Failed to clean thread locals", e); } } /* * Clears the given thread local map object. Also pass in the field that * points to the internal table to save re-calculating it on every * call to this method. */ private static void clearThreadLocalMap(Object map, Field internalTableField) throws NoSuchMethodException, IllegalAccessException, NoSuchFieldException, InvocationTargetException { if (map != null) { Method mapRemove = map.getClass().getDeclaredMethod("remove", ThreadLocal.class); mapRemove.setAccessible(true); Object[] table = (Object[]) internalTableField.get(map); int staleEntriesCount = 0; if (table != null) { for (int j = 0; j < table.length; j++) { if (table[j] != null) { boolean remove = false; // Check the key Object key = ((Reference<?>) table[j]).get(); // Check the value Field valueField = table[j].getClass().getDeclaredField("value"); valueField.setAccessible(true); Object value = valueField.get(table[j]); if ((value != null && CleanableValue.class.isAssignableFrom(value.getClass()))) { remove = true; } if (remove) { Object[] args = new Object[4]; if (key != null) { args[0] = key.getClass().getCanonicalName(); args[1] = key.toString(); } args[2] = value.getClass().getCanonicalName(); args[3] = value.toString(); if (logger.isTraceEnabled()) { logger.trace("ThreadLocal with key of type [{}] (value [{}]) and a value of type [{}] (value [{}]): The ThreadLocal has been forcibly removed.", args); } if (key == null) { staleEntriesCount++; } else { mapRemove.invoke(map, key); } } } } } if (staleEntriesCount > 0) { Method mapRemoveStale = map.getClass().getDeclaredMethod("expungeStaleEntries"); mapRemoveStale.setAccessible(true); mapRemoveStale.invoke(map); } } } /* * Get the set of current threads as an array. */ private static Thread[] getThreads() { // Get the current thread group ThreadGroup tg = Thread.currentThread().getThreadGroup(); // Find the root thread group while (tg.getParent() != null) { tg = tg.getParent(); } int threadCountGuess = tg.activeCount() + 50; Thread[] threads = new Thread[threadCountGuess]; int threadCountActual = tg.enumerate(threads); // Make sure we don't miss any threads while (threadCountActual == threadCountGuess) { threadCountGuess *= 2; threads = new Thread[threadCountGuess]; // Note tg.enumerate(Thread[]) silently ignores any threads that // can't fit into the array threadCountActual = tg.enumerate(threads); } return threads; } }