Java: список локаторов строк?
Есть ли способ перечислить TheadLocals, связанные с потоком? В идеале я мог бы получить доступ к карте Thread.threadLocals, но она защищена пакетом.
По этой причине мне нужно проверить потоки, поскольку они возвращаются в пул потоков, чтобы гарантировать, что ThreadLocals были правильно очищены. Возможно, есть еще один способ сделать это?
Ответы
Ответ 1
Список ThreadLocals и Очистка ThreadLocals может быть выполнена посредством используя отражение (и флаг setAccessible()), чтобы переопределить обычные разрешения JVM. По очевидным причинам это недоступно, когда все механизмы безопасности на месте.
Ответ 2
Вы можете использовать метод afterExecute
пула потоков для выполнения любой очистки (reinitializiation?), если вы знаете, какие переменные вы хотите очистить.
В противном случае вы можете использовать отражение - изнутри потока, итерации по объявленным полям интересующего вас класса (ы), и для каждого из них, тип которого является экземпляром ThreadLocal, set
он соответствует его initialValue
на интересующем вас объекте.
Ответ 3
В строках "Еще один хороший способ сделать это" я создал оболочку Runnable, которая принимает снимок ранее существовавших локаторов потоков, запускает вложенную runnable, а затем очищает (устанавливает нуль) любой поток, который был не изначально присутствующих.
Это можно было бы лучше сделать, поместив код "моментальный снимок" в подклассифицированный Executor beforeExecute() и код "cleanup" в afterExecute, как было предложено @danben.
В любом случае, красота заключается в том, что вам не нужно жестко программировать, какие локаторы потоков сохраняются или отбрасываются.
Обработка исключений была удалена из списка источников, чтобы избежать беспорядка.
public class ThreadLocalCleaningRunnable implements Runnable
{
private final Runnable runnable;
public ThreadLocalCleaningRunnable(Runnable runnable) {
this.runnable = nonNull(runnable);
}
public void run() {
// printThreadLocals();
Set> initialThreadLocalKeys = getThreadLocalKeys();
try {
runnable.run();
}
finally {
cleanThreadLocalsExcept(initialThreadLocalKeys);
// printThreadLocals();
}
}
public static void printThreadLocals() {
Thread thread = Thread.currentThread();
Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
threadLocalsField.setAccessible(true);
Class threadLocalMapKlazz = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
Field tableField = threadLocalMapKlazz.getDeclaredField("table");
tableField.setAccessible(true);
Object threadLocals = threadLocalsField.get(thread);
if (threadLocals != null) {
Object table = tableField.get(threadLocals);
if (table != null) {
int threadLocalCount = Array.getLength(table);
String threadName = thread.getName();
for (int i = 0; i > getThreadLocalKeys() {
Thread thread = Thread.currentThread();
Set> threadLocalKeys = new HashSet>();
Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
threadLocalsField.setAccessible(true);
Class threadLocalMapKlazz = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
Field tableField = threadLocalMapKlazz.getDeclaredField("table");
tableField.setAccessible(true);
Object threadLocals = threadLocalsField.get(thread);
if (threadLocals != null) {
Object table = tableField.get(threadLocals);
if (table != null) {
int threadLocalCount = Array.getLength(table);
for (int i = 0; i ) entry).get();
if (o instanceof ThreadLocal) {
threadLocalKeys.add((ThreadLocal) o);
}
}
}
}
}
return threadLocalKeys;
}
public static void cleanThreadLocalsExcept(Set> keptThreadLocalKeys) {
Thread thread = Thread.currentThread();
Field threadLocalsField = Thread.class.getDeclaredField("threadLocals");
threadLocalsField.setAccessible(true);
Class threadLocalMapKlazz = Class.forName("java.lang.ThreadLocal$ThreadLocalMap");
Field tableField = threadLocalMapKlazz.getDeclaredField("table");
tableField.setAccessible(true);
Object threadLocals = threadLocalsField.get(thread);
if (threadLocals != null) {
Object table = tableField.get(threadLocals);
if (table != null) {
int threadLocalCount = Array.getLength(table);
for (int i = 0; i ) entry).get();
if (o instanceof ThreadLocal) {
ThreadLocal tl = (ThreadLocal) o;
if (!keptThreadLocalKeys.contains(tl)) {
Field valueField = entry.getClass().getDeclaredField("value");
valueField.setAccessible(true);
valueField.set(entry, null);
}
}
}
}
}
}
}
}
Ответ 4
По этой причине мне нужно проверить потоки, поскольку они возвращаются в пул потоков, чтобы гарантировать, что ThreadLocals были правильно очищены.
Лично я считаю, что использование локальных данных потока в потоках threadpool является плохой практикой. Если вам действительно нужно локальное состояние потока, вы должны самостоятельно управлять потоками, чтобы вы могли явно очищать данные.
Потоки пула потоков имеют неопределенное время жизни, поэтому вам не следует полагаться на явно управляемые данные локального потока.
Ответ 5
Вы можете использовать AOP-подобную конструкцию, создав реализацию Runnable, которая обертывает исходную Runnable вашей собственной реализацией. Он будет вызывать исходный Runnable run method, а затем выполнить любую другую очистку, которая вам нужна из контекста потока, что позволит вам вызвать метод ThreadLocal.remove(). Затем дайте эту оболочку пулу потоков. Это будет работать для любой реализации пула потоков (например, без методов/послеExecute)
Ответ 6
Из источников это выглядит довольно жестко. Все закрыто ни Thread, ни ThreadLocal.
Возможно, вы сможете сделать то, что вам нужно, с помощью инструментария, переопределив ThreadLocal, чтобы добавить метод, который будет выгружать локали в текущем потоке.
Вот пример, который я нашел для добавления журнала в существующий класс: http://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html
Вам нужно будет использовать BCEL или JavaAssist, чтобы исправить байт-код ThreadLocal, чтобы добавить метод. Позже вам нужно будет использовать отражение, чтобы получить дескриптор метода, чтобы вы могли его назвать.
Примечание: это, вероятно, не будет работать, если вы работаете в ограниченной среде (апплет или сервер приложений), поскольку механизмы безопасности обычно не позволяют вам сбрасывать системные классы.