[java] 자바 : 스레드 지역 목록?
Listing ThreadLocals and Clearing ThreadLocals can be accomplished by using reflection (and the setAccessible() flag) to override the JVM's usual permissions. For obvious reasons, this not available when all security mechanisms are in place.
-------------------You could use the afterExecute
method of the thread pool to do whatever cleanup (reinitializiation?), as long as you know which variables you want to clean up.
Otherwise you could use reflection - from within the thread, iterate through the declared fields of the class(es) youre interested in, and for each one whose type is an instance of ThreadLocal, set
it to its initialValue
on the object(s) you care about.
Along the lines of 'Another good way to do this', I made a Runnable wrapper which takes a snapshot of the pre-existing thread locals, runs the nested runnable, then clears (sets to null) any thread local which was not initially present.
This could be better done by putting the 'snapshot' code into a subclassed Executor's beforeExecute() and the 'cleanup' code in the afterExecute as suggested by @danben.
Either way, the beauty is that you don't have to hard-code which thread locals to keep or discard.
Exception handling was removed from the source listing to avoid clutter.
public class ThreadLocalCleaningRunnable implements Runnable
{
private final Runnable runnable;
public ThreadLocalCleaningRunnable(Runnable runnable) {
this.runnable = nonNull(runnable);
}
public void run() {
// printThreadLocals();
Set<ThreadLocal<?>> 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 < threadLocalCount; i++) {
Object entry = Array.get(table, i);
if (entry instanceof Reference) {
Field valueField = entry.getClass().getDeclaredField("value");
valueField.setAccessible(true);
Object value = valueField.get(entry);
if (value != null) {
System.err.print("thread ["+ threadName +"] local "+ value.getClass().getName() +" "+ value.toString());
}
}
}
}
}
}
public static Set<ThreadLocal<?>> getThreadLocalKeys() {
Thread thread = Thread.currentThread();
Set<ThreadLocal<?>> threadLocalKeys = new HashSet<ThreadLocal<?>>();
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 < threadLocalCount; i++) {
Object entry = Array.get(table, i);
if (entry instanceof Reference) {
Object o = ((Reference<?>) entry).get();
if (o instanceof ThreadLocal) {
threadLocalKeys.add((ThreadLocal<?>) o);
}
}
}
}
}
return threadLocalKeys;
}
public static void cleanThreadLocalsExcept(Set<ThreadLocal<?>> 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 < threadLocalCount; i++) {
Object entry = Array.get(table, i);
if (entry instanceof Reference) {
Object o = ((Reference<?>) 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);
}
}
}
}
}
}
}
}
-------------------The reason I need this is I need to inspect threads as they are returned to a thread pool to ensure the ThreadLocals have been properly cleaned up.
Personally, I think using thread local data on threadpool threads is a bad practice. If you really need thread local state, you should be self managing the threads, so you can explicitly cleanup the data.
Thread pool threads have indeterminate lifetimes, so you shouldn't be relying on explicitly managed local thread data.
-------------------You could use AOP-like construct, by creating a Runnable implementation that wraps the original Runnable by your own implementation. It will invoke the original Runnable's run method and then perform any other cleanup you need from within the thread's context, which will allow you to call the ThreadLocal.remove() method. Then, give this wrapper to the thread pool. This would work for any thread pool implementation (e.g. ones without before/afterExecute methods)
-------------------출처에서 보면 꽤 빡빡한 것 같습니다. 모든 것은 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 바이트 코드를 패치해야합니다. 나중에 리플렉션을 사용하여 메서드를 호출 할 수 있도록 메서드에 대한 핸들을 가져와야합니다.
참고 : 보안 메커니즘이 일반적으로 시스템 클래스를 조작하는 것을 방지하므로 제한된 환경 (애플릿 또는 앱 서버)에서 실행중인 경우에는 작동하지 않을 수 있습니다.
출처
https://stackoverflow.com/questions/2002786