Is there no way to iterate over or copy all the values of a Java ThreadLocal?
Asked Answered
C

3

8

Context:

static ThreadLocal<MyType> threadLocalMyType = ...

What i'd like is to say something like:

for (ThreadLocalEntry e: threadLocalMyType.getMapLikeThing() {
    // Thread t = e.getKey(); 
    // I don't need the thread value right now, but it might be useful for 
    // something else. 

    MyType theMyType = e.getValue();
    // [...do something with theMyType...]
}
Conductivity answered 8/5, 2010 at 20:6 Comment(5)
Perhaps a more interesting question is why you want to do this ?Mosquito
Well, the idea, good or bad, was having a way to collect lists of changes in multiple threads, and then at intervals perform all the changes. It looked like a natural way to avoid a synchronization that is not really needed, since the changes are only ordered wrt to a specific thread.Conductivity
See my answer to a related question; if you need to access all the values in a ThreadLocal, you don't want a ThreadLocal at all. Generally, you want a ConcurrentHashMap<Thread, MyType>.Phycomycete
@Phycomycete Yes, that's what I went with as I commented elsewhere. For better 'fidelity' compared to how a ThreadLocal solution would have worked regarding retention behaviour, I suppose a synchronized variant of WeakHashMap<Thread, MyType> could be used instead.Conductivity
Sure, or a Cache.Phycomycete
P
7

One way would be to handle this manually:

  • use a wrapper of ThreadLocal (extend it)
  • whenever a value is set, keep a (static) Map of Threads and values

Alternatively, with some reflection (getDeclaredMethod() and setAccessible(true)), you can:

  • call Thread.getThreads()
  • call yourThreadLocal.getMap(thread) (for each of the above threads)
  • call map.getEntry(yourThreadLocal)

The 1st is more preferable.

Prent answered 8/5, 2010 at 20:30 Comment(4)
I guess, in short, that the answer to the question is 'No', and the solution for me would be simply "static ConcurrentHashMap<Thread, MyType>"...although that forsakes the optimization of ThreadLocal.Conductivity
Thanks for the discussion, doublep and Bozho; if I had reps...I'll accept the Bozho answer because it outlines two solutions, although since I don't have to use ThreadLocal I'll go with a ConcurrentHashMap instead.Conductivity
@Jonas: If you use your ThreadLocal mostly for getting and not for setting, you could have it both way. As I understand that's what Bozho proposed: when setting a value, also duplicate it in some other map. Then the thread can get from ThreadLocal (efficiently), while other threads could read from the synchronized (and thus less efficient) secondary storage. Though if that value points to some object, consider if access to it needs additional synchronization.Shf
Yes, it's strictly for getting, so it would indeed be more efficient. And yes, I think I can't avoid the synchronization; because at the 'interval activation points' (what to call it :) the MyType would be mutated (a list is emptied or replaced with empty list). I think the price of the synchronization would be quite low though, since contention could only happen at those points. And I shouldn't overengineer like this :)Conductivity
S
1

No, because internally it is implement differently: each thread has a map-like thing of its locals. What you want to do would be inherently thread-unsafe if ThreadLocal allowed it. Each thread obviously doesn't use any kind of synchronization when accessing its own locals: no other thread can do that, so synchronization is not needed. For this reason, accessing the locals map from any other thread (if that was possible) would be thread-unsafe.

As Bozho suggested, you could do that by subclassing ThreadLocal and duplicating values somewhere else. Don't forget to synchronize access to that "somewhere else" properly.

Shf answered 8/5, 2010 at 20:36 Comment(2)
I realize the class is for a single purpose, thread-local objects; so one can't really blame the ThreadLocal designer/s for not including a mechanism to do what I want here (I'm pretty sure it could be done thread-safely, but would likely incur lowered optimization, and it kind of conflicts with the name 'Local', as I interpret what you said above). I hadn't understood it fully before asking the question, I'm afraid.Conductivity
Yes, as I see ThreadLocal could implement this feature, but then it would have to synchronize access to the internal map, thus adding unneeded overhead for the common case.Shf
A
0

I came acrosss the same problem and after seeing the answers here, I decided to use a hybrid approach:

public class PersistentThreadLocal<T> extends ThreadLocal<T> {

    final Map<Thread, T> allValues;
    final Supplier<? extends T> valueGetter;

    public PersistentThreadLocal(Supplier<? extends T> initialValue) {
        this(0, initialValue);
    }

    public PersistentThreadLocal(int numThreads, Supplier<? extends T> initialValue) {
        allValues = Collections.synchronizedMap(
            numThreads > 0 ? new WeakHashMap<>(numThreads) : new WeakHashMap<>()
        );
        valueGetter = initialValue;
    }

    @Override
    protected T initialValue() {
        T value = valueGetter != null ? valueGetter.get() : super.initialValue();
        allValues.put(Thread.currentThread(), value);
        return value;
    }

    @Override
    public void set(T value) {
        super.set(value);
        allValues.put(Thread.currentThread(), value);
    }

    @Override
    public void remove() {
        super.remove();
        allValues.remove(Thread.currentThread());
    }

    public Collection<T> getAll() {
        return allValues.values();
    }

    public void clear() {
        allValues.clear();
    }
}

EDIT: if you plan to use this with a ThreadPoolExecutor, change the WeakHashMap to a regular HashMap, otherwise strange things will happen!

Aluminiferous answered 13/11, 2017 at 15:28 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.