这里是基于java JDK1.8源码分析的。
首先从整体上描述一下ThreadLocal:
ThreadLocal中的ThreadLocalMap静态内部类使用的是线性探测表(散列表)作为数据结构。
每一个Thread对象都持有一个ThreadLocalMap,该Map以ThreadLocal对象的引用作为Key来查询/保存/删除Value。
一个线程中可以使用多个ThreadLocal对象来保存不同的数据,不同的线程可以使用相同的ThreadLocal对象作为Key,但是由于每个线程持有的ThreadLocalMap对象不同,所有相同Key对应的Value不同。做个类比,每个线程对应不同的学生,ThreadLocal是一个个页码,线程持有的ThreadLocalMap是不同学生持有不同版本的字典,当老师说了一个页码以后,不同学生在自己手上独一无二的字典中查找到的内容当然不同。构造器中并没有做什么
public ThreadLocal() { }ThreadLocal的get方法回返回当前线程中该ThreadLocal实例对应保存的数据。
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(); }方法中,首先获取当前线程t,然后得到t的ThreadLocalMap。ThreaLocalMap是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; } } /** * The initial capacity -- MUST be a power of two. */ private static final int INITIAL_CAPACITY = 16; /** * The table, resized as necessary. * table.length MUST always be a power of two. */ private Entry[] table; ..... }内部有一个继承了弱引用的Entry静态类,我们可以看到Entry内部其实是Key为弱引用,关于会造成的内存泄露的分析,大家可以看一下这篇文章
接着上面所说,如果该Map不为空,则以该ThreadLocal实例的引用作为Key在该Map中寻找Entry并返回Value;如果Map为空则调用setInitialValue方法返回数据。下面我们看一下getEntry方法:
private Entry getEntry(ThreadLocal<?> key) { int i = key.threadLocalHashCode & (table.length - 1); Entry e = table[i]; if (e != null && e.get() == key) return e; else return getEntryAfterMiss(key, i, e); }由于ThreadLocalMap内部是用线性探测的散列表实现的(底层维护一个Entry数组table[],区别于HashMap等容器的拉链表),这里很清楚地看到由key的hashcode&数组长度 得到下标之后,分为查找中和没有查找中两种情况,这里不再详细说明了。我们回到上面,如果Map为空,则调用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; }首先调用initialValue方法获得了一个Value
protected T initialValue() { return null; }该方法为空,一般是子类继承ThreadLocal类重写这个方法返回一个自己想保存的数据的初始值。回到setInitialValue方法中,接下来也是获得当前线程,再获得ThreadLocalMap,如果Map不空,进行set;如果Map空,新建Map:
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }在这个方法中我们为Thread的ThreadLocal.ThreadLocalMap引用新建一个对象:
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); }这里我们把调用get方法的ThreadLocal对象和在initialValue方法中返回的值作为一个Entry存入线性探测表中。
我们总结一下,当调用了某个ThreadLocal.get()后:
1.查找当前线程是否具有ThreadLocalMap,有进入2,没有进入3.
2.在Map中以该ThreadLocal对象为Key查找Value并返回(涉及到线性探测表的查找)。
3.以该ThreadLocal对象为Key,initialValue方法中返回值作为Value作为一个Entry,为当前线程初始化一个ThreadLocalMap。
ThreadLocal的set方法为当前线程设置数据。
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) map.set(this, value); else createMap(t, value); }依旧是和Map相关的,我们看一下ThreadLocalMap的set操作,这里依旧涉及到了线性探测表这个数据结构的set操作,我们就不具体展开了。
与jdk1.8中的略有不同,下面粗略地看一下
底层也是维护了一个数组
static class Values { /** * Size must always be a power of 2. */ private static final int INITIAL_SIZE = 16; /** * Placeholder for deleted entries. */ private static final Object TOMBSTONE = new Object(); /** * Map entries. Contains alternating keys (ThreadLocal) and values. * The length is always a power of 2. */ private Object[] table; ..... }看一下它的put方法,没有具体细看,但是注意到key和value是前后放入table数组中的:
void put(ThreadLocal<?> key, Object value) { cleanUp(); // Keep track of first tombstone. That's where we want to go back // and add an entry if necessary. int firstTombstone = -1; for (int index = key.hash & mask;; index = next(index)) { Object k = table[index]; if (k == key.reference) { // Replace existing entry. table[index + 1] = value; return; } if (k == null) { if (firstTombstone == -1) { // Fill in null slot. table[index] = key.reference; table[index + 1] = value; size++; return; } // Go back and replace first tombstone. table[firstTombstone] = key.reference; table[firstTombstone + 1] = value; tombstones--; size++; return; } // Remember first tombstone. if (firstTombstone == -1 && k == TOMBSTONE) { firstTombstone = index; } } }同样,在Thread类中,维护一个Values成员:
ThreadLocal.Values localValues;根据当前线程去获取其Values,如果values为空,为values初始化,否则以该ThreadLocal实例为key,传入参数为value,前后一起放入数组中。
public void set(T value) { Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values == null) { values = initializeValues(currentThread); } values.put(this, value); } /** * Gets Values instance for this thread and variable type. */ Values values(Thread current) { return current.localValues; } /** * Creates Values instance for this thread and variable type. */ Values initializeValues(Thread current) { return current.localValues = new Values(); }也是获取到当前Thread的Values对象,如果以当前ThreadLocal对象为key找到了对应的value则返回,否则调用getAfterMiss方法,这个方法没有细看,应该也是使用了线性探测法去找key。
@SuppressWarnings("unchecked") public T get() { // Optimized for the fast path. Thread currentThread = Thread.currentThread(); Values values = values(currentThread); if (values != null) { Object[] table = values.table; int index = hash & values.mask; if (this.reference == table[index]) { return (T) table[index + 1]; } } else { values = initializeValues(currentThread); } return (T) values.getAfterMiss(this); }对于ThreadLocal的总结请看开头。
