前面《Thread源码理解》一节讲了Thread的源码,因为是第一次写博客,难免有点拘泥于格式,本来想下面这张写一下我对线程池实现的理解,但是今天重新翻出了我的处女作,发现Thread源码中有ThreadLocal的变量:
#1
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
一直以来都是把ThreadLocal当作同一线程中顺序存取线程变量的容器,当看到Thread类中ThreadLocal变量=null且没有使用,则Thread类中定义ThreadLocals 变量是用来做什么的?后面会详细的分析ThreadLocals变量的用法。
一、ThreadLocalMap
首先,ThreadLocalMap是一个静态内部类,在ThreadLocal中定义了静态Map,此种设计属于组合模式(六原则一法则)。
(1)
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
-------------------------------------------
(2)
private static final int INITIAL_CAPACITY = 16;
private Entry[] table;
private int size = 0;
private int threshold; // Default to 0
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
--------------------------------------------
(3)
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);
}
1、从上面的源码1中可以知道ThreadLocalMap是内部数组Entry为弱引用(WeakReference)的Map,且该引用为ThreadLocal<?>(弱引用为发生GC时清除对应对象,该处设计的很巧妙,认为线程执行时间小于两次gc间隔时间,则一个线程内数据是能够顺序获取的。)
2、从2中知道Entry初始大小为16,负载因子为2/3。
3、在3中主要是设置线程在ThreadLocalMap对应槽的值。
有这么一段代码:
table[i] = new Entry(firstKey, firstValue);
因对应槽中是线程的资源作为key,当线程销毁时,对应的Entry[i]不会被销毁,而其对应的线程已经消失,此时需要清除这个Entry,而因为采用了弱引用,JVM会帮忙清除对应Entry(这也是1中所说的设计巧妙的地方,避免产生内存泄露)。
4、上面分析了Entry的实现,那现在有这么一种情况,当线程为线程池中的线程时,我们知道线程池中的线程terminated的状态后不会被销毁,当新的请求到来时会作为其work线程继续处理任务,那此时,如果该Entry的数据没有被GC,该ThreadLocal中存了上个请求的数据,所以当在线程池中使用ThreadLocal时,记得清除数据。
二、ThreadLocal的实现
一中对ThreadLocal中的Map的实现进行了分析,通过Map的槽存储了以ThreadLocal为key的键值对,那是如何实现的呢?
在ThreadLocal中有两个基本方法:
1)set方法
(1)
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
------------------------------------------------
(2)
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
------------------------------------------------
(3)
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
//
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
------------------------------------------
(4)
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
1、(1)中getMap((Thread)t),第一次调用时,参考#1中Thread中ThreadLocals=nu l l,第一次获取map=null,调用(4)new ThreadLocalMap(this, firstValue),见一(3)分析。
2、后续set对应线程值时,直接对ThreadLocalMap的槽操作。
3、replaceStaleEntry、cleanSomeSlots这两个方法是用来处理key为null时清除脏数据,暂时先不分析。
2)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();
}
---------------------------------------------------
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;
}
-----------------------------------------------------
protected T initialValue() {
return null;
}
get方法比较简单,就是获取桶中的对应ThreadLocal的value,如果为null就返回初始值(也是null,哈哈,点个赞吧)。
三、inheritableThreadLocals实现
前面讲了ThreadLocal的实现,同时也了解了Thread类中的ThreadLocals的作用和使用。在此基础上,来分析一下inheritableThreadLocals的实现。
以下面源码为引:
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
上面是Thread中init的实现,当父inheritableThreadLocals 存在的时候,也就是子线程可继承时,调用ThreadLocal.createInheritedMap方法,从下面的源码中能够看到,子ThreadLocals初始化时,是对父inheritableThreadLocals的hash桶进行了完全复制:
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
-------------------------------------------------------
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}
网友评论