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...]
}
Nick Bastin
  • 30,415
  • 7
  • 59
  • 78
Jonas N
  • 1,757
  • 2
  • 21
  • 41
  • 2
    Perhaps a more interesting question is why you want to do this ? – Brian Agnew May 08 '10 at 20:09
  • 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. – Jonas N May 08 '10 at 20:16
  • See [my answer](http://stackoverflow.com/a/15654081/113632) 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`. – dimo414 Aug 17 '15 at 15:48
  • @dimo414 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 could be used instead. – Jonas N Aug 19 '15 at 16:53
  • Sure, or a [`Cache`](https://code.google.com/p/guava-libraries/wiki/CachesExplained#Reference-based_Eviction). – dimo414 Aug 19 '15 at 17:07

3 Answers3

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.

Bozho
  • 588,226
  • 146
  • 1,060
  • 1,140
  • 1
    I guess, in short, that the answer to the question is 'No', and the solution for me would be simply "static ConcurrentHashMap"...although that forsakes the optimization of ThreadLocal. – Jonas N May 08 '10 at 20:42
  • 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. – Jonas N May 08 '10 at 21:07
  • 1
    @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. –  May 08 '10 at 21:09
  • 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 :) – Jonas N May 08 '10 at 21:19
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.

  • 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. – Jonas N May 08 '10 at 21:03
  • 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. –  May 08 '10 at 21:07
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!

Sina Madani
  • 1,246
  • 3
  • 15
  • 27