跨不同线程的ThreadLocal值访问

JHo*_*nti 11 java multithreading

假设ThreadLocal变量为不同的线程保存不同的值,是否可以从另一个线程访问一个ThreadLocal变量的值?

即在下面的示例代码中,是否可以在t1中从t2读取TLocWrapper.tlint的值?

public class Example
{
  public static void main (String[] args)
  {
    Tex t1 = new Tex("t1"), t2 = new Tex("t2");
    new Thread(t1).start();
    try
    {
      Thread.sleep(100);
    }
    catch (InterruptedException e)
    {}
    new Thread(t2).start();
    try
    {
      Thread.sleep(1000);
    }
    catch (InterruptedException e)
    {}
    t1.kill = true;
    t2.kill = true;
  }

  private static class Tex implements Runnable
  {
    final String name;
    Tex (String name)
    {
      this.name = name;
    }
    public boolean kill = false;
    public void run ()
    {
      TLocWrapper.get().tlint.set(System.currentTimeMillis());
      while (!kill)
      {
        // read value of tlint from TLocWrapper
        System.out.println(name + ": " + TLocWrapper.get().tlint.get());
      }
    }
  }
}
class TLocWrapper
{
  public ThreadLocal<Long> tlint = new ThreadLocal<Long>();
  static final TLocWrapper self = new TLocWrapper();
  static TLocWrapper get ()
  {
    return self;
  }
  private TLocWrapper () {}
}
Run Code Online (Sandbox Code Playgroud)

And*_*yle 16

正如彼得所说,这是不可能的.如果你想要这种功能,那么从概念上讲你真正想要的只是一个标准Map<Thread, Long>- 大多数操作都是用密钥完成的Thread.currentThread(),但如果你愿意,你可以传入其他线程.

但是,这可能不是一个好主意.首先,持有对垂死线程的引用会使GC陷入困境,所以你必须通过额外的箍来制作密钥类型WeakReference<Thread>.而且我不相信a Thread无论如何都是一个很好的Map键.

因此,一旦超出了烘焙的便利性ThreadLocal,也许值得质疑的是,使用Thread对象作为关键是最佳选择吗?为每个线程提供唯一ID(字符串或整数,如果它们还没有更有意义的自然键)可能会更好,并且只需使用这些 ID 来关闭地图.我知道你的例子是人为的,但你可以做同样的事情用Map<String, Long>和使用按键"t1""t2".

它也可以说是更清晰,因为a Map表示你实际使用数据结构的方式; ThreadLocals更像是标量变量,具有一些访问控制魔法而不是集合,因此即使可以根据需要使用它们,对于查看代码的其他人来说也可能更加困惑.


Fre*_*ger 9

根据Andrzej Doyle的答案,这里有一个完整的解决方案:

ThreadLocal<String> threadLocal = new ThreadLocal<String>();
threadLocal.set("Test"); // do this in otherThread

Thread otherThread = Thread.currentThread(); // get a reference to the otherThread somehow (this is just for demo)

Field field = Thread.class.getDeclaredField("threadLocals");
field.setAccessible(true);
Object map = field.get(otherThread);

Method method = Class.forName("java.lang.ThreadLocal$ThreadLocalMap").getDeclaredMethod("getEntry", ThreadLocal.class);
method.setAccessible(true);
WeakReference entry = (WeakReference) method.invoke(map, threadLocal);

Field valueField = Class.forName("java.lang.ThreadLocal$ThreadLocalMap$Entry").getDeclaredField("value");
valueField.setAccessible(true);
Object value = valueField.get(entry);

System.out.println("value: " + value); // prints: "value: Test"
Run Code Online (Sandbox Code Playgroud)

所有以前的评论当然仍然适用 - 它不安全!

但是出于调试目的,它可能正是你所需要的 - 我就这样使用它.


use*_*048 6

我想看看ThreadLocal存储中的内容,所以我扩展了上面的例子来向我展示.也方便调试.

            Field field = Thread.class.getDeclaredField("threadLocals");
            field.setAccessible(true);
            Object map = field.get(Thread.currentThread());
            Field table = Class.forName("java.lang.ThreadLocal$ThreadLocalMap").getDeclaredField("table");
            table.setAccessible(true);
            Object tbl = table.get(map);
            int length = Array.getLength(tbl);
            for(int i = 0; i < length; i++) {                   
                Object entry = Array.get(tbl, i);
                Object value = null;
                String valueClass = null;
                if(entry != null) { 
                    Field valueField = Class.forName("java.lang.ThreadLocal$ThreadLocalMap$Entry").getDeclaredField("value");
                    valueField.setAccessible(true);
                    value = valueField.get(entry);
                    if(value != null) {
                        valueClass = value.getClass().getName();
                    }
                    Logger.getRootLogger().info("[" + i + "] type[" + valueClass + "] " + value);
                }
            }
Run Code Online (Sandbox Code Playgroud)


Pet*_*rey 5

只有将相同的值放在一个非ThreadLocal的字段中才能进行访问.根据定义,ThreadLocal仅对该线程是本地的.


bes*_*sss 5

ThreadLocalMap可以通过Reflection等访问Thread.class.getDeclaredField("threadLocals") setAccssible(true).但是,不要这样做.预计映射只能由拥有的线程访问,并且访问ThreadLocal的任何值都是潜在的数据竞争.

但是,如果您可以与所述数据竞赛一起生活,或者只是避开它们(更好的想法).这是最简单的解决方案.扩展线程并定义你需要的任何东西,就是这样:

ThreadX extends Thread{
  int extraField1;
  String blah2; //and so on
}
Run Code Online (Sandbox Code Playgroud)

这是一个不错的解决方案,它不依赖于WeakReferences,但需要您创建线程.你可以这样设置((ThreadX)Thread.currentThread()).extraField1=22

确保在访问字段时不进行展览数据竞赛.所以你可能需要volatile,synchronized等等.

整体地图是一个terribad的想法,永远不会保持对你没有明确管理/拥有的对象的引用; 特别是当涉及到Thread,ThreadGroup,Class,ClassLoader ...... WeakHashMap<Thread, Object>稍微好一点时,你需要专门访问它(即在锁定下),这可能会降低多线程环境中的性能.WeakHashMap不是世界上最快的东西.

ConcurrentMap,Object>会更好,但是你需要一个具有equals和的WeakRef hashCode......