为什么在Thread中使用map来存储ThreadLocal对象?



我正在阅读ThreadLocal的源代码。我得到了一个无法理解的问题。这是为什么使用映射来存储ThreadLocal对象?我认为使用数组更合适。因为您可以从零开始定义ThreadLocal的哈希码。你不需要担心键的碰撞。有人能帮我吗?提前感谢!

我想我明白你的意思了。

内部线程局部变量通过为每个线程局部变量生成一个内部键来工作。在最近的实现中(我正在研究Java 11),键是由散列算法生成的,并在每个线程用来存储线程本地值的(自定义)映射中使用。

您建议将这些映射替换为简单的值数组,并将键生成为顺序递增的整数,以便它们可以用于数组的下标。我猜你认为它会提高性能和/或减少内存。


问题是你的想法无法扩展。

假设一个应用程序创建了许多ThreadLocal实例。现在假设一个线程为某个随机的i设置iThreadLocal的值。

  • 在当前的实现中,在线程的哈希表中创建了一个条目。这将占用固定的空间。

  • 在你提议的实现中,线程的数组现在需要有至少i插槽,其中i是关键。(Java数组不是稀疏的!)如果还不存在,则必须创建长度为i的对象。如果存在,可能需要重新分配长度为i的内存

ThreadLocal实例的数量足够大,并且它们是随机设置的,并且对于任何给定的线程只设置几个,那么基于数组的表示所使用的内存将大于当前哈希表表示。

然后,考虑当ThreadLocal被垃圾收集时会发生什么。

  • 在当前的哈希表实现中,每个map条目是(扩展)一个WeakReference,其ref引用ThreadLocal。当给定的ThreadLocal实例被垃圾收集时,该ThreadLocal的所有线程映射中的所有条目将使其ref字段为空。map的实现可以识别任何"过时"的条目,当它遇到过时的条目时,它将其从表中删除。

  • 在您建议的实现中,没有WeakReference要打破。即使可以解决这个问题,也很难看出如何压缩数组。


现在,从事Java实现的Sun/Oracle工程师都是聪明人。多年来实施和维护ThreadLocal的特定工程师可能会寻找优化它的方法。他们可能在某些时候考虑过使用数组。但我想他们会考虑所有的利弊1…在决定每个线程使用哈希表的当前方法之前。

也值得了解当前实现的背景。在这个针对JDK 1.4代码库的(修复的)bug报告中有一些线索:https://bugs.java.com/bugdatabase/view_bug.do?bug_id=4414045

1 -这些很可能包括其他问题,而不是上面的…

在map中存储threadLocals的原因没有在任何文档中指定,据我所知。

我认为原因是您希望防止每个线程存储对同一个threadLocal的多个引用,这可以通过Map轻松完成。

最新更新