ThreadLocal的是如何实现的? 难道是用Java实现(使用线程ID一些并发的地图对象),它使用一些JVM挂钩更有效地做到这一点?
Answer 1:
这里所有的答案是正确的,但有点令人失望,因为他们有些粉饰多么聪明ThreadLocal
的实现。 我只是在看源代码ThreadLocal
和它是如何实现的惊喜了深刻的印象。
朴素的实现
如果我问你实现ThreadLocal<T>
给出的Javadoc描述的API类,你会怎么办? 初始执行可能会是一个ConcurrentHashMap<Thread,T>
使用Thread.currentThread()
作为它的关键。 这将会工作得相当好,但也有一些缺点。
- 线程争-
ConcurrentHashMap
是一个非常聪明的类,但它最终还是要处理防止多个线程以任何方式与它摆弄,如果不同的线程经常打它,会出现放缓。 - 永久保持的指针主题和对象都,即使在线程完成,并且可以GC'ed。
在GC-友好实施
好了再试,通过让处理的垃圾收集问题的弱引用 。 在WeakReferences与处理可能会造成混淆,但它应该是足够使用内置像这样的地图:
Collections.synchronizedMap(new WeakHashMap<Thread, T>())
或者,如果我们使用番石榴 (我们应该!):
new MapMaker().weakKeys().makeMap()
这意味着,一旦没有其他人持有到线程(这意味着它的结束)键/值可以被垃圾收集,这是一种进步,但仍然没有解决线程争用问题,这意味着到目前为止我们ThreadLocal
是不是所有的这惊人的一类。 此外,如果有人决定守住Thread
对象,他们会结束后,他们从来没有被GC'ed,因此也不会给我们的对象,即使他们现在已经在技术上无法访问。
聪明的实现
我们一直在思考ThreadLocal
的线程来值的映射,但也许这不是真正想它的正确途径。 而不是在每一个ThreadLocal的对象,如果我们认为它什么作为的ThreadLocal的对象映射到值中的每个线程想到它是从线程的映射值? 如果每个线程存储的映射,ThreadLocal的只是提供了一个很好的界面进入该映射,我们能够避免所有的以前的实现的问题。
一个实现将是这个样子:
// called for each thread, and updated by the ThreadLocal instance
new WeakHashMap<ThreadLocal,T>()
有没有必要担心并发这里,因为只有一个线程将被访问该映射。
Java的开发者有一个重要优势我们在这里 - 他们可以直接开发Thread类,并添加字段和操作它,而这正是他们做了什么。
在java.lang.Thread
有下面几行:
/* ThreadLocal values pertaining to this thread. This map is maintained * by the ThreadLocal class. */ ThreadLocal.ThreadLocalMap threadLocals = null;
其作为意见建议确实是一切价值的一包专用映射通过被跟踪ThreadLocal
这个对象Thread
。 实施ThreadLocalMap
不是WeakHashMap
,但是也遵循相同的基本合同,包括弱引用持有其键。
ThreadLocal.get()
然后实施,像这样:
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()
像这样:
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
对象。 这样一来,我们永远不必担心其他线程的值( ThreadLocal
字面只能访问当前线程的值),因此没有并发问题。 此外,一旦Thread
完成,它的地图将自动GC'ed,所有的本地对象将被清理。 即使Thread
被扶住,在ThreadLocal
对象由弱引用举行,并能尽快清理ThreadLocal
对象超出范围。
不用说,我是通过这个实现相当深刻的印象,它很优雅周围很多的并发问题得到(诚然,采取的是Java核心部分的优势,但是这是可以原谅他们,因为它是这样一个聪明类),并允许快速和到只需要对象的线程安全访问由一个线程在同一时间访问。
TL;博士 ThreadLocal
的实现是很酷,和更快/比你想象的第一眼聪明。
如果你喜欢这个答案你可能也欣赏我的(不太详细) 讨论ThreadLocalRandom
。
Thread
/ ThreadLocal
取自代码片段甲骨文/的OpenJDK的Java实现8 。
Answer 2:
你的意思是java.lang.ThreadLocal
。 这是很简单的,真的,这只是一个地图存储在每个内部名称-值对Thread
对象(见Thread.threadLocals
场)。 该API隐藏了实现细节,但是这都或多或少有它。
Answer 3:
在Java中的ThreadLocal变量工作通过访问Thread.currentThread()实例举行一个HashMap。
Answer 4:
假设你要实现ThreadLocal
,你怎么让它线程的具体情况? 当然,最简单的方法是在Thread类来创建一个非静态字段,我们称之为threadLocals
。 因为每个线程由一个线程实例表示,因此threadLocals
在每个线程会有所不同了。 这也是Java的做什么:
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
什么是ThreadLocal.ThreadLocalMap
这里? 因为你只有一个threadLocals
一个线程,所以如果你简单地采取threadLocals
为您的ThreadLocal
(比如说,定义为threadLocals Integer
),你只会有一个ThreadLocal
特定线程。 如果你想多ThreadLocal
一个线程变量? 最简单的方法就是让threadLocals
一个HashMap
,在key
的每个条目的是名ThreadLocal
变量, value
中的每个条目的是价值ThreadLocal
变量。 有点混乱? 比方说,我们有两个线程, t1
和t2
。 他们采取同样的Runnable
实例作为参数Thread
构造,并且他们有两个ThreadLocal
命名变量tlA
和tlb
。 这是什么滋味。
t1.tlA
+-----+-------+
| Key | Value |
+-----+-------+
| tlA | 0 |
| tlB | 1 |
+-----+-------+
t2.tlB
+-----+-------+
| Key | Value |
+-----+-------+
| tlA | 2 |
| tlB | 3 |
+-----+-------+
请注意,该值由我做了。
现在看来完美。 但是,什么是ThreadLocal.ThreadLocalMap
? 为什么不只是使用HashMap
? 为了解决这个问题,让我们来看看,当我们通过设置一个值会发生什么情况set(T value)
的方法ThreadLocal
类:
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
getMap(t)
简单的返回t.threadLocals
。 由于t.threadLocals
被initilized到null
,所以我们进入createMap(t, value)
第一:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
它创建了一个新的ThreadLocalMap
使用当前实例ThreadLocal
实例,并要设置的值。 让我们来看看ThreadLocalMap
是什么样子,它在事实上的部分ThreadLocal
类
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
...
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
...
}
所述的核心部分ThreadLocalMap
类是Entry class
,它扩展WeakReference
。 它可以确保如果当前线程退出,将垃圾自动收集。 这就是为什么它使用ThreadLocalMap
,而不是一个简单的HashMap
。 它通过当前ThreadLocal
和其作为参数值Entry
级,所以当我们想要得到的值,我们可以得到它的table
,这是一个实例Entry
类:
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();
}
这是什么样的,在整个画面:
Answer 5:
从概念上讲,你能想到的一个ThreadLocal<T>
作为拿着Map<Thread,T>
存储线程SPECI科幻C值,虽然这不是它是如何切实执行。
线程SPECI音响C值被存储在线程对象本身; 当线程终止时,线程SPECI音响C值可以是垃圾收集。
参考: JCIP