Is there no way to iterate over or copy all the values of a Java ThreadLocal?
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.
MyT开发者_运维知识库ype theMyType = e.getValue();
// [...do something with theMyType...]
}
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.
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 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!
精彩评论