为什么java.lang.ThreadLocal是Thread上的一个映射而不是ThreadLocal?

时间:2009-12-01 21:28:01

标签: java thread-local resource-leak

天真地,我期望ThreadLocal是某种类型的Thread的WeakHashMap。当我得知ThreadLocal的值实际上是saved in a map in the Thread时,我有点困惑。为什么这样做?如果值保存在ThreadLocal本身中,我希望与ThreadLocal关联的resource leaks不会存在。

澄清:我在考虑像

这样的东西
public class AlternativeThreadLocal<T> { 
    private final Map<Thread, T> values = 
        Collections.synchronizedMap(new WeakHashMap<Thread, T>());
    public void set(T value) { values.put(Thread.currentThread(), value); }
    public T get() { return values.get(Thread.currentThread());}    
}

据我所知,这可以防止一个奇怪的问题,即如果值以某种方式强烈引用ThreadLocal本身,那么ThreadLocal和剩下的值都不会被垃圾收集,直到Thread死掉。 (当ThreadLocal是值引用的类上的静态变量时,可能会出现最狡猾的形式。现在,在应用程序服务器的重新部署中存在大量资源泄漏,因为无论是对象还是类都不能被收集。)

4 个答案:

答案 0 :(得分:8)

有时你只是问一个问题就能得到启发。 :-)现在我刚看到一个可能的答案:线程安全。如果带有值的映射在Thread对象中,则插入新值通常是线程安全的。如果映射在ThreadLocal上,则会出现通常的并发问题,这可能会减慢速度。 (当然你会使用ReadWriteLock而不是同步,但问题仍然存在。)

答案 1 :(得分:4)

您似乎误解了ThreadLocal泄漏的问题。重复使用相同的线程(例如在线程池中)时会发生ThreadLocal泄漏,并且在使用之间不会清除ThreadLocal状态。当Thread被销毁时,它们不是ThreadLocal剩余的结果,因为除了线程本身之外没有任何东西引用ThreadLocal Map。

对线程本地对象使用Thread的弱引用映射不会阻止ThreadLocal泄漏问题,因为线程仍存在于线程池中,因此线程本地对象在从线程重用线程时不适合收集池。您仍然需要手动清除ThreadLocal以避免泄漏。

正如您在答案中所说,并发控制得以简化,ThreadLocal Map是每个线程的单个实例。它还使一个线程无法访问另一个线程的本地对象,如果ThreadLocal对象在您建议的Map上公开了API,则可能不是这种情况。

答案 2 :(得分:0)

我记得几年前Sun将线程局部变量的实现改为当前形式。我不记得它是什么版本以及旧的impl是什么样的。

无论如何,对于每个线程应该有一个插槽的变量,Thread是自然容器的选择。如果可以,我们还将直接添加我们的线程局部变量作为Thread类的成员。

答案 3 :(得分:0)

为什么Map会在ThreadLocal上?这没有多大意义。那么它是ThreadLocal中对象的ThreadLocal s的映射吗?

这是对象Thread的映射的简单原因是:

  1. 这是一个实现细节,即Map不会以任何方式暴露;
  2. 总是很容易找出当前线程(Thread.currentThread())。
  3. 另外一个想法是ThreadLocal可以为每个使用它的Thread存储不同的值,因此它基于Thread是有意义的,不是吗?