从声明位置以外的 class 访问 Java ThreadLocal 对象

Accessing Java ThreadLocal object from a class other than where it was declared

我正在声明一个 ThreadLocal 对象并设置如下值。

Public Class Blah {

private ThreadLocal<Set<Integer>> numberThreaLocalObj= new ThreadLocal<>();

  void setValue() {
    Set<Integer> numberSet = new HashSet<>();
    numberSet .add(1);
    threaLocalObj.set(numberSet) 
  }
} 

是否可以在同一线程内在此 class 之外引用此 numberThreaLocalObj 变量?

我发现一些代码似乎清除了所有线程局部变量,但我只需要根据条件清除这个特定的线程局部变量。

不幸的是,这是一个继承的技术设计。

编辑 - 找到了我的回答中概述的解决方案。

它是一个线程本地的事实是无关紧要的。您在问:我可以从另一个 class 访问私有字段吗?

答案是:并非如此。如果您有想要访问此字段的 Blah 实例(它是一个非静态字段;因此,Blah 的每个实例都有一个本地线程),您可以使用 java.lang.reflection:

Field f = Blah.class.getDeclaredField("numberThreaLocalObj");
f.setAccessible(true);
ThreadLocal<?> t = f.get(someInstanceOfBlah);
t.set(null);

只要你添加适当的异常守卫就可以做到。

我可以使用下面的代码来识别我感兴趣的特定线程局部对象 代码来自之前的 post,由@lyaffe https://whosebug.com/users/509566/lyaffe

回答

我的线程局部对象在另一个class

 private ThreadLocal<Set<Integer>> numberSetTL = new NamedThreadLocal<>("MY_NAMED_TL_NAME");

我稍微修改了代码以删除特定名称。要删除的 ThreadLocal 必须是 Spring NamedThreadLocal 对象。

 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);
                    if(threadLocal instanceof NamedThreadLocal) {

                        if("MY_NAMED_TL_NAME".equalsIgnoreCase(threadLocal.toString())) {
                            threadLocal.remove();
                            LOG.debug(tlName + " - ThreadLocal found and removed.");
                        }

                }
                }
            }
        } catch(Exception e) {
            // We will tolerate an exception here and just log it
            throw new IllegalStateException(e);
        }
    }

您可以在 class 中将 ThreadLocal 变量设置为 Static 并在整个应用程序中全局访问它。来自官方 Spring 文档的示例

https://github.com/spring-projects/spring-data-keyvalue-examples/blob/00e1930be9d8c056f654c685e01eaa2ce49f0beb/retwisj/src/main/java/org/springframework/data/redis/samples/retwisj/RetwisSecurity.java#L26

不要忘记清除 ThreadLocalExecutor 处理程序中的 ThreadLocal 变量(使用 afterExecute() ,它将在 Thread 释放到 ThreadPool 后执行),如下面的第 5.1 节所示 link:

https://www.baeldung.com/java-threadlocal