rip*_*234 74 java multithreading thread-static thread-local
ThreadLocal是如何实现的?它是用Java实现的(使用从ThreadID到对象的一些并发映射),还是使用一些JVM钩子来更有效地执行它?
dim*_*414 108
这里的所有答案都是正确的,但有点令人失望,因为他们在某种程度上掩盖了聪明ThreadLocal的实施方式.我只是在查看源代码,ThreadLocal并对它的实现方式印象深刻.
天真的实施
如果我要求你实现一个ThreadLocal<T>给定javadoc中描述的API 的类,你会怎么做?初始实施可能是一个ConcurrentHashMap<Thread,T>使用Thread.currentThread()作为其关键.这将相当不错,但确实有一些缺点.
ConcurrentHashMap是一个相当聪明的类,但它最终仍然必须处理防止多个线程以任何方式与它混合,并且如果不同的线程经常命中它,将会有减速.GC友好的实施
好了再试一次,让我们使用弱引用处理垃圾收集问题.处理WeakReferences可能会令人困惑,但使用如此构建的映射应该足够了:
Collections.synchronizedMap(new WeakHashMap<Thread, T>())
Run Code Online (Sandbox Code Playgroud)
或者,如果我们使用番石榴(我们应该!):
new MapMaker().weakKeys().makeMap()
Run Code Online (Sandbox Code Playgroud)
这意味着一旦没有其他人持有线程(暗示它已经完成),键/值可以被垃圾收集,这是一个改进,但仍然没有解决线程争用问题,这意味着到目前为止我们ThreadLocal不是所有的一流的惊人.此外,如果有人决定在Thread完成后抓住物体,他们就永远不会被GC,因此我们的物体也不会,即使它们现在在技术上无法到达.
聪明的实施
我们一直在考虑ThreadLocal将线程映射到值,但也许这不是考虑它的正确方法.而不是将其视为从Threads到每个ThreadLocal对象中的值的映射,如果我们将其视为ThreadLocal对象到每个Thread中的值的映射,该怎么办?如果每个线程都存储了映射,并且ThreadLocal只是为该映射提供了一个很好的接口,我们可以避免以前实现的所有问题.
实现看起来像这样:
// called for each thread, and updated by the ThreadLocal instance
new WeakHashMap<ThreadLocal,T>()
Run Code Online (Sandbox Code Playgroud)
这里没有必要担心并发性,因为只有一个线程会访问这个地图.
Java开发人员在这方面比我们有一个很大的优势 - 他们可以直接开发Thread类并为其添加字段和操作,这正是他们所做的.
在java.lang.Thread那里有以下几行:
Run Code Online (Sandbox Code Playgroud)/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
正如评论所暗示的那样,确实是ThreadLocal对象跟踪的所有值的包私有映射Thread.执行ThreadLocalMap不是一个WeakHashMap,但它遵循相同的基本合同,包括通过弱引用来保持其键.
ThreadLocal.get() 然后像这样实现:
Run Code Online (Sandbox Code Playgroud)public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }
而ThreadLocal.setInitialValue()像这样:
Run Code Online (Sandbox Code Playgroud)private T setInitialValue() { T value = initialValue(); Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); return value; }
基本上,使用此线程中的映射来保存所有ThreadLocal对象.这样,我们就不必担心其他Threads中的值(ThreadLocal字面上只能访问当前Thread中的值),因此没有并发问题.此外,一旦Thread完成,其地图将自动进行GC,并清除所有本地对象.即使Thread被保持ThreadLocal住,对象也被弱引用保持,并且一旦ThreadLocal对象超出范围就可以清除.
毋庸置疑,我对这个实现印象深刻,它非常优雅地解决了许多并发问题(诚然通过利用成为核心Java的一部分,但是因为它是如此聪明的类而可以原谅它们)并允许快速和线程安全访问只需要一次一个线程访问的对象.
tl;博士 ThreadLocal的实施非常酷,比你初看起来的速度更快/更聪明.
如果您喜欢这个答案,您可能也会欣赏我(不太详细)的讨论ThreadLocalRandom.
Thread/ ThreadLocal代码片段取自Oracle/OpenJDK的Java 8实现.
ska*_*man 32
你的意思java.lang.ThreadLocal.它非常简单,实际上,它只是存储在每个Thread对象中的名称 - 值对的映射(参见Thread.threadLocals字段).API隐藏了实现细节,但这或多或少都与它有关.
Java中的ThreadLocal变量通过访问Thread.currentThread()实例持有的HashMap来工作.