跨不同线程的ThreadLocal值访问
假设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 tlint = new ThreadLocal(); static final TLocWrapper self = new TLocWrapper(); static TLocWrapper get () { return self; } private TLocWrapper () {} }
正如彼得所说,这是不可能的。 如果你想要这种function,那么从概念上讲你真正想要的只是一个标准的Map
– 其中大多数操作将使用Thread.currentThread()
的键来完成,但你可以传入其他线程,如果你希望。
但是,这可能不是一个好主意。 首先,持有对垂死线程的引用会使GC陷入混乱,所以你必须通过额外的环来制作密钥类型WeakReference
。 而且我还不相信Thread
无论如何都是一个很棒的Map键。
因此,一旦超出了烘焙ThreadLocal
的便利性,也许值得质疑是否使用Thread
对象作为关键是最佳选择? 为每个线程提供唯一ID(字符串或整数,如果它们还没有更有意义的自然键)可能会更好,并且只需使用这些 ID来关闭地图。 我意识到你的例子是人为的,但你可以使用Map
并使用"t1"
和"t2"
键来做同样的事情。
由于Map
表示您实际使用数据结构的方式,因此它也可以说更清晰; ThreadLocals更像是标量变量,具有一些访问控制魔法而不是集合,因此即使可以根据需要使用它们,对于查看代码的其他人来说也可能更加困惑。
根据Andrzej Doyle的答案,这里有一个完整的解决方案:
ThreadLocal threadLocal = new ThreadLocal (); 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仅对该线程是本地的。
ThreadLocalMap
可以通过Reflection和Thread.class.getDeclaredField("threadLocals")
setAccssible(true)
,依此类推。 但是,不要这样做。 预计映射只能由拥有的线程访问,并且访问ThreadLocal的任何值都是潜在的数据竞争。
但是,如果你可以与所述数据竞赛一起生活,或者只是避免它们(更好的想法)。 这是最简单的解决方案。 扩展线程并定义你需要的任何东西,就是这样:
ThreadX extends Thread{ int extraField1; String blah2; //and so on }
这是一个不错的解决方案,它不依赖于WeakReferences,但需要您创建线程。 你可以这样设置((ThreadX)Thread.currentThread()).extraField1=22
确保在访问字段时不进行展览数据竞赛。 所以你可能需要volatile,synchronized等等。
整体地图是一个terribad的想法,永远不会保持对你没有明确管理/拥有的对象的引用; 特别是当涉及到Thread,ThreadGroup,Class,ClassLoader …… WeakHashMap
稍微好一点,但是你需要独占访问它(即在锁定下),这可能会在严重的multithreading环境中阻碍性能。 WeakHashMap不是世界上最快的东西。
ConcurrentMap,Object>会更好但是你需要一个具有equals
和hashCode
的WeakRef …
我想看看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); } }
- Java调试:找不到源代码
- 在自动触发的默认侦听器之前,激活Hibernate自定义事件侦听器
- 列表实现:LinkedList与ArrayList和TreeList相比真的表现不佳吗?
- java.lang.NoClassDefFoundError:Intellij中的org / hamcrest / SelfDescribing
- Knuth-Morris-Pratt算法
- BigDecimal movePointRight和scaleByPowerOfTen有什么区别?
- 从JAR目录中读取属性文件
- Comparable.compareTo的返回值在Java中意味着什么?
- sessionfactory.openSession()和sessionfactory.openStatelessSession()之间的区别?