我正在阅读ThreadLocal的源代码。我得到了一个无法理解的问题。这是为什么使用映射来存储ThreadLocal对象?我认为使用数组更合适。因为您可以从零开始定义ThreadLocal的哈希码。你不需要担心键的碰撞。有人能帮我吗?提前感谢!
我想我明白你的意思了。
内部线程局部变量通过为每个线程局部变量生成一个内部键来工作。在最近的实现中(我正在研究Java 11),键是由散列算法生成的,并在每个线程用来存储线程本地值的(自定义)映射中使用。
您建议将这些映射替换为简单的值数组,并将键生成为顺序递增的整数,以便它们可以用于数组的下标。我猜你认为它会提高性能和/或减少内存。
问题是你的想法无法扩展。
假设一个应用程序创建了许多ThreadLocal
实例。现在假设一个线程为某个随机的i
设置i
和ThreadLocal
的值。
-
在当前的实现中,在线程的哈希表中创建了一个条目。这将占用固定的空间。
-
在你提议的实现中,线程的数组现在需要有至少
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轻松完成。