java 跨不同线程的 ThreadLocal 值访问
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/5180114/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
ThreadLocal value access across different threads
提问by JHollanti
Given that a ThreadLocal variable holds different values for different threads, is it possible to access the value of one ThreadLocal variable from another thread?
鉴于 ThreadLocal 变量为不同的线程保存不同的值,是否可以从另一个线程访问一个 ThreadLocal 变量的值?
I.e. in the example code below, is it possible in t1to read the value of TLocWrapper.tlintfrom t2?
即在下面的示例代码中,是否可以在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 () {}
}
回答by Andrzej Doyle
As Peter says, this isn't possible. If you want this sort of functionality, then conceptually what you really want is just a standard Map<Thread, Long>
- where mostoperations will be done with a key of Thread.currentThread()
, but you can pass in other threads if you wish.
正如彼得所说,这是不可能的。如果您想要这种功能,那么从概念上讲,您真正想要的只是一个标准Map<Thread, Long>
-大多数操作将使用 键完成Thread.currentThread()
,但如果您愿意,您可以传入其他线程。
However, this likely isn't a great idea. For one, holding a reference to moribund threads is going to mess up GC, so you'd have to go through the extra hoop of making the key type WeakReference<Thread>
instead. And I'm not convinced that a Thread
is a great Map key anyway.
然而,这可能不是一个好主意。一方面,持有对垂死线程的引用会弄乱 GC,因此您必须通过额外的圈子来WeakReference<Thread>
代替生成键类型。而且我不相信 aThread
是一个很好的 Map 键。
So once you go beyond the convenience of the baked-in ThreadLocal
, perhaps it's worth questioning whether using a Thread
object as the key is the best option? It might be better to give each threads unique IDs (Strings or ints, if they don't already have natural keys that make more sense), and simply use theseto key the map off. I realise your example is contrived, but you could do the same thing with a Map<String, Long>
and using keys of "t1"
and "t2"
.
因此,一旦您超越了 bake-in 的便利性ThreadLocal
,也许值得质疑使用Thread
对象作为 key 是否是最佳选择?为每个线程提供唯一的 ID(字符串或整数,如果它们还没有更有意义的自然键)可能会更好,并简单地使用它们来关闭映射。我知道你的例子是人为的,但你可以做同样的事情用Map<String, Long>
和使用按键"t1"
和"t2"
。
It would also arguably be clearer since a Map
represents how you're actually using the data structure; ThreadLocals are more like scalar variables with a bit of access-control magic than a collection, so even if it were possible to use them as you want it would likely be more confusing for other people looking at your code.
它也可以说更清晰,因为 aMap
代表您实际使用数据结构的方式;ThreadLocals 更像是带有一点访问控制魔法的标量变量,而不是集合,因此即使可以根据需要使用它们,但其他人在查看您的代码时可能会更加困惑。
回答by Frederic Leitenberger
Based on the answer of Andrzej Doyle here a full working solution:
基于 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"
All the previous comments still apply of course - it's not safe!
当然,之前的所有评论仍然适用 -这不安全!
But for debugging purposes it might be just what you need - I use it that way.
但出于调试目的,它可能正是您所需要的 - 我就是这样使用它的。
回答by user738048
I wanted to see what was in ThreadLocal storage, so I extended the above example to show me. Also handy for debugging.
我想看看 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);
}
}
回答by Peter Lawrey
It only possible if you place the same value in a field which is not ThreadLocal and access that instead. A ThreadLocal by definition is only local to that thread.
只有在不是 ThreadLocal 的字段中放置相同的值并访问它时才有可能。根据定义,ThreadLocal 仅对该线程是本地的。
回答by bestsss
ThreadLocalMap
CAN be access via Reflection and Thread.class.getDeclaredField("threadLocals")
setAccssible(true)
, and so on.
Do not do that, though. The map is expected to be accessed by the owning thread only and accessing any value of a ThreadLocal is a potential data race.
ThreadLocalMap
可以通过 Reflection 和 等方式访问Thread.class.getDeclaredField("threadLocals")
setAccssible(true)
。不过不要那样做。该映射预计只能由拥有线程访问,并且访问 ThreadLocal 的任何值都是潜在的数据竞争。
However, if you can live w/ the said data races, or just avoid them (way better idea). Here is the simplest solution. Extend Thread and define whatever you need there, that's it:
但是,如果您可以忍受上述数据竞争,或者只是避免它们(更好的主意)。这是最简单的解决方案。扩展线程并在那里定义您需要的任何内容,就是这样:
ThreadX extends Thread{
int extraField1;
String blah2; //and so on
}
That's a decent solution that doesn't relies on WeakReferences but requires that you create the threads. You can set like that ((ThreadX)Thread.currentThread()).extraField1=22
这是一个不错的解决方案,它不依赖于 WeakReferences,但需要您创建线程。你可以这样设置((ThreadX)Thread.currentThread()).extraField1=22
Make sure you do no exhibit data races while accessing the fields. So you might need volatile, synchronized and so on.
确保在访问字段时没有出现数据竞争。所以你可能需要 volatile、synchronized 等等。
Overall Map is a terribad idea, never keep references to object you do not manage/own explicitly; especially when it comes to Thread, ThreadGroup, Class, ClassLoader... WeakHashMap<Thread, Object>
is slightly better, however you need to access it exclusively (i.e. under lock) which might damper the performance in heavily multithreaded environment. WeakHashMap is not the fastest thing in the world.
总体而言 Map 是一个糟糕的想法,永远不要保留对您没有明确管理/拥有的对象的引用;特别是当涉及到 Thread、ThreadGroup、Class、ClassLoader... WeakHashMap<Thread, Object>
稍微好一点时,但是您需要以独占方式访问它(即锁定),这可能会影响多线程环境中的性能。WeakHashMap 并不是世界上最快的东西。
ConcurrentMap, Object> would be better but you need a WeakRef that has equals
and hashCode
...
ConcurrentMap, Object> 会更好,但您需要一个 WeakRef 具有equals
和hashCode
...