Java 跨不同线程的线程本地值访问

Java 跨不同线程的线程本地值访问,java,multithreading,Java,Multithreading,假设一个ThreadLocal变量为不同的线程持有不同的值,那么是否可以从另一个线程访问一个ThreadLocal变量的值 即,在下面的示例代码中,在t1中是否可以从t2读取TLocWrapper.tlint的值 公共类示例 { 公共静态void main(字符串[]args) { Tex t1=新Tex(“t1”),t2=新Tex(“t2”); 新线程(t1.start(); 尝试 { 睡眠(100); } 捕捉(中断异常e) {} 新线程(t2.start(); 尝试 { 睡眠(1000);

假设一个ThreadLocal变量为不同的线程持有不同的值,那么是否可以从另一个线程访问一个ThreadLocal变量的值

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

公共类示例
{
公共静态void main(字符串[]args)
{
Tex t1=新Tex(“t1”),t2=新Tex(“t2”);
新线程(t1.start();
尝试
{
睡眠(100);
}
捕捉(中断异常e)
{}
新线程(t2.start();
尝试
{
睡眠(1000);
}
捕捉(中断异常e)
{}
t1.kill=true;
t2.kill=true;
}
私有静态类Tex实现Runnable
{
最后的字符串名;
Tex(字符串名)
{
this.name=名称;
}
公共布尔kill=false;
公开作废运行()
{
TLocWrapper.get().tlint.set(System.currentTimeMillis());
而(!杀死)
{
//从TLocWrapper读取tlint的值
System.out.println(name+“:”+TLocWrapper.get().tlint.get());
}
}
}
}
类TLocWrapper
{
public ThreadLocal tlint=new ThreadLocal();
静态最终TLocWrapper self=新TLocWrapper();
静态TLocWrapper get()
{
回归自我;
}
私有TLocWrapper(){}
}

只有将相同的值放在非ThreadLocal的字段中并访问该字段时,才可能执行此操作。根据定义,ThreadLocal只是该线程的本地线程。

正如Peter所说,这是不可能的。如果您想要这种功能,那么从概念上讲,您真正想要的只是一个标准的
映射
——其中大多数操作将使用
Thread.currentThread()
键来完成,但是如果您愿意,您可以传入其他线程

然而,这可能不是一个好主意。首先,持有一个对垂死线程的引用将搞乱GC,因此您必须经历额外的困难,转而使用键类型
WeakReference
。我不认为
线程是一个很好的映射键

因此,一旦您超越了烘焙的
ThreadLocal
的便利性,也许有必要质疑使用
Thread
对象作为键是否是最佳选择?最好为每个线程提供唯一的ID(字符串或int,如果它们还没有更合理的自然键),并简单地使用这些来设置映射的键。我知道你的例子是人为设计的,但是你可以用
地图
“t1”
“t2”
键做同样的事情


它也可以说更清晰,因为
映射
表示实际使用数据结构的方式;ThreadLocals更像是具有访问控制魔力的标量变量,而不是集合,因此,即使可以根据您的需要使用它们,也可能会让其他查看您的代码的人更加困惑。

ThreadLocalMap
可以通过反射和
Thread.class.getDeclaredField(“ThreadLocals”)进行访问
设置可访问性(true)
,依此类推。 不过,不要那样做。该映射预计只能由所属线程访问,而访问ThreadLocal的任何值都是一种潜在的数据竞争

然而,如果你能与上述数据竞赛共存,或者只是避免它们(更好的办法)。这是最简单的解决方案。扩展线程并定义所需的内容,即:

ThreadX extends Thread{
  int extraField1;
  String blah2; //and so on
}
这是一个不错的解决方案,它不依赖于weakreference,而是需要创建线程。您可以这样设置
((ThreadX)Thread.currentThread()).extraField1=22

确保在访问字段时没有显示数据竞争。所以您可能需要volatile、synchronized等等

整体映射是一个坏主意,永远不要保留对您未明确管理/拥有的对象的引用;特别是当涉及到线程、线程组、类、类加载器
WeakHashMap
稍好一些,但是您需要以独占方式访问它(即在锁定状态下),这可能会影响多线程环境中的性能。WeakHashMap不是世界上最快的东西


ConcurrentMap、Object>会更好,但您需要一个具有
等于
哈希代码
的WeakRef.

基于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"
ThreadLocal ThreadLocal=new ThreadLocal();
threadLocal.set(“测试”);//在其他线程中执行此操作
Thread otherThread=Thread.currentThread();//以某种方式获取对otherThread的引用(这只是为了演示)
Field=Thread.class.getDeclaredField(“threadLocals”);
字段。setAccessible(true);
对象映射=field.get(其他线程);
方法Method=Class.forName(“java.lang.ThreadLocal$ThreadLocalMap”).getDeclaredMethod(“getEntry”,ThreadLocal.Class);
方法setAccessible(true);
WeakReference条目=(WeakReference)方法.invoke(map,threadLocal);
Field valueField=Class.forName(“java.lang.ThreadLocal$ThreadLocalMap$Entry”).getDeclaredField(“值”);
valueField.setAccessible(true);
对象值=valueField.get(条目);
System.out.println(“值:”+value);//打印:“值:测试”
当然,前面的所有评论仍然适用-这不安全


但出于调试目的,它可能正是您所需要的—我就是这样使用的。

我想看看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);
                }
            }
Field-Field=Thread.class.getDeclaredField(“threadLocals”);
字段。setAccessible(true);
对象映射=field.get(Thread.currentThread());
Field table=Class.forName(“java.lang.ThreadLocal$ThreadLocalMap”).getDeclaredField(“表格”);
表1.setAccessible(true);
对象tbl=table.get(map);
int length=Array.getLength(tbl);
for(int i=0;i            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);
                }
            }