【问题标题】:Is there no way to iterate over or copy all the values of a Java ThreadLocal?有没有办法遍历或复制 Java ThreadLocal 的所有值?
【发布时间】:2010-05-08 20:06:55
【问题描述】:

上下文:

static ThreadLocal<MyType> threadLocalMyType = ...

我想说的是:

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...]
}

【问题讨论】:

  • 也许一个更有趣的问题是你为什么要这样做?
  • 好吧,不管好坏,这个想法是有一种方法可以收集多个线程中的更改列表,然后每隔一段时间执行所有更改。它看起来像是一种避免真正需要的同步的自然方法,因为更改仅针对特定线程进行排序。
  • 查看my answer 相关问题;如果您需要访问ThreadLocal 中的所有值,则根本不需要ThreadLocal。通常,您需要ConcurrentHashMap&lt;Thread, MyType&gt;
  • @dimo414 是的,这就是我在其他地方评论时所采用的。与 ThreadLocal 解决方案在保留行为方面的工作方式相比,为了获得更好的“保真度”,我想可以改用 Wea​​kHashMap 的同步变体。
  • 当然,或者Cache

标签: java multithreading thread-local


【解决方案1】:

一种方法是手动处理:

  • 使用ThreadLocal 的包装器(扩展它)
  • 无论何时设置一个值,都保留一个 (static) Map 的线程和值

或者,通过一些反思(getDeclaredMethod()setAccessible(true)),您可以:

  • 致电Thread.getThreads()
  • 调用yourThreadLocal.getMap(thread)(针对上述每个线程)
  • 致电map.getEntry(yourThreadLocal)

第一个更好。

【讨论】:

  • 我想,简而言之,这个问题的答案是“否”,而对我来说,解决方案就是“静态 ConcurrentHashMap”......尽管这放弃了优化的 ThreadLocal。
  • 感谢 doublep 和 Bozho 的讨论;如果我有代表...我会接受 Bozho 的答案,因为它概述了两种解决方案,尽管由于我没有必须使用 ThreadLocal,我将改用 ConcurrentHashMap。
  • @Jonas:如果您将ThreadLocal 主要用于获取而不是设置,那么您可以同时使用它。据我了解,这就是 Bozho 的建议:当 设置 一个值时,也要在其他地图中复制它。然后线程可以从ThreadLocal(有效地)get,而其他线程可以从同步的(因此效率较低的)辅助存储中读取。尽管如果该值指向某个对象,请考虑对其的访问是否需要额外的同步。
  • 是的,它只是为了获取,所以它确实会更有效率。是的,我认为我无法避免同步;因为在“间隔激活点”(如何称呼它:) MyType 将发生突变(列表被清空或替换为空列表)。我认为同步的代价会相当低,因为争用只会发生在这些点上。而且我不应该像这样过度设计:)
【解决方案2】:

不,因为在内部它的实现方式不同:每个线程都有一个类似于地图的本地对象。如果ThreadLocal 允许,您想要做的是固有的线程不安全。每个线程在访问自己的本地时显然不使用任何类型的同步:没有其他线程可以做到这一点,因此不需要同步。因此,从任何其他线程(如果可能的话)访问本地映射将是线程不安全的。

正如 Bozho 建议的那样,您可以通过子类化 ThreadLocal 并在其他地方复制值来做到这一点。不要忘记正确同步对“其他地方”的访问。

【讨论】:

  • 我意识到这个类是为了单一目的,线程本地对象;所以不能真的责怪 ThreadLocal 设计师/s 没有包括一种机制来做我想做的事情(我很确定它可以在线程安全的情况下完成,但可能会降低优化,并且它与名称“本地”,正如我解释你上面所说的那样)。恐怕在问这个问题之前我还没有完全理解。
  • 是的,正如我所见,ThreadLocal 可以实现此功能,但是它必须同步对内部地图的访问,从而为常见情况增加了不必要的开销。
【解决方案3】:

我遇到了同样的问题,在看到这里的答案后,我决定使用混合方法:

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();
    }
}

编辑:如果您打算将其与 ThreadPoolExecutor 一起使用,请将 WeakHashMap 更改为常规 HashMap,否则会发生奇怪的事情!

【讨论】:

    猜你喜欢
    • 2010-09-14
    • 1970-01-01
    • 1970-01-01
    • 2021-11-02
    • 2010-11-20
    • 2020-11-27
    • 2022-01-09
    • 1970-01-01
    相关资源
    最近更新 更多