java:列出线程本地?

时间:2010-01-04 18:45:29

标签: java multithreading

有没有办法列出绑定到线程的TheadLocals?理想情况下,我可以访问Thread.threadLocals映射,但它受包保护。

我需要这个的原因是我需要检查线程,因为它们被返回到线程池以确保已正确清理ThreadLocals。也许有另一种方法可以做到这一点?

6 个答案:

答案 0 :(得分:11)

Listing ThreadLocalsClearing ThreadLocals可以通过使用反射(和the setAccessible() flag)来覆盖JVM的常用权限来完成。出于显而易见的原因,当所有安全机制到位时,这都不可用。

答案 1 :(得分:2)

只要知道要清理哪些变量,就可以使用线程池的afterExecute方法进行清理(重新初始化?)。

否则你可以使用反射 - 从线程内部迭代你感兴趣的类的声明字段,对于每个类型是ThreadLocal实例的每个字段,set它到它的{ {1}}关于你关心的对象。

答案 2 :(得分:2)

沿着'另一个好方法',我创建了一个Runnable包装器,它获取了预先存在的线程本地的快照,运行嵌套的runnable,然后清除(设置为null)本地的任何线程最初没有出现。

这可以通过将'snapshot'代码放入子类Executor的beforeExecute()和@danben建议的afterExecute中的'cleanup'代码来做得更好。

无论哪种方式,美丽的是你不必硬编码保留或丢弃哪个线程本地。

从源列表中删除了异常处理以避免混乱。

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);
                                }
                            }
                        }
                    }
                }
            }
    }
}

答案 3 :(得分:1)

  

我需要这个的原因是我需要检查线程,因为它们被返回到线程池以确保已正确清理ThreadLocals。

就我个人而言,我认为在线程池线程上使用线程本地数据是一种不好的做法。如果你真的需要线程本地状态,你应该自行管理线程,这样你就可以明确地清理数据。

线程池线程具有不确定的生命周期,因此您不应该依赖显式托管的本地线程数据。

答案 4 :(得分:0)

您可以通过创建一个Runnable实现来使用类似AOP的构造,该实现通过您自己的实现包装原始Runnable。它将调用原始Runnable的run方法,然后在线程的上下文中执行您需要的任何其他清理,这将允许您调用ThreadLocal.remove()方法。然后,将此包装器提供给线程池。这适用于任何线程池实现(例如没有before / afterExecute方法的那些)

答案 5 :(得分:0)

从消息来源来看,它看起来非常紧张。一切都是Thread或ThreadLocal的私有。

可能能够通过一个检测代理执行您需要的操作,方法是重新定义ThreadLocal以添加一个将当地线程转储本地的方法。

以下是我发现的向现有类添加日志记录的示例:http://today.java.net/pub/a/today/2008/04/24/add-logging-at-class-load-time-with-instrumentation.html

您需要使用BCELJavaAssist修补ThreadLocal字节码以添​​加方法。稍后,您需要使用反射来获取方法的句柄,以便您可以调用它。

注意:如果您在受限制的环境(applet或appserver)中运行,这可能不起作用,因为安全机制通常会阻止您查看系统类。