ThreadLocal value access across different threads

前端 未结 5 975
南笙
南笙 2020-12-09 19:29

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?

相关标签:
5条回答
  • 2020-12-09 19:58

    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.

    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

    Make sure you do no exhibit data races while accessing the fields. So you might need volatile, synchronized and so on.

    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.

    ConcurrentMap, Object> would be better but you need a WeakRef that has equals and hashCode...

    0 讨论(0)
  • 2020-12-09 19:59

    Based on the answer of Andrzej Doyle here a full working solution:

    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.

    0 讨论(0)
  • 2020-12-09 20:01

    I wanted to see what was in ThreadLocal storage, so I extended the above example to show me. Also handy for debugging.

                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);
                    }
                }
    
    0 讨论(0)
  • 2020-12-09 20:02

    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 most operations will be done with a key of Thread.currentThread(), but you can pass in other threads if you wish.

    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.

    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 these to 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".

    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.

    0 讨论(0)
  • 2020-12-09 20:19

    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.

    0 讨论(0)
提交回复
热议问题