ThreadLocal对象用法如下
ThreadLocal tl =new ThreadLocal();public voidmultiThreadMethod(T t) tl.set(t);
T t2 = tl.get();
这样就可以做到每个线程可以存取自己的T对象。
但是这种实现的原理是什么呢。
其实这里做到数据的线程隔离方法很简单,看一下Thread类即可发现如下代码。
ThreadLocal.ThreadLocalMap threadLocals = null;
看这个很眼熟是吧,其实就是每个线程内都拥有一个ThreadLocalMap的引用,如果该线程内没有使用
ThreadLocal的话则保持null,如果一旦使用了ThreadLocal对象并调用set(Tt)方法,则会new一个
ThreadLocalMap对象,并为map赋初值,代码如下。
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方法如下
ThreadLocalMap getMap(Thread t)
return t.threadLocals;
createMap方法如下
void createMap(Thread t, T firstValue)
t.threadLocals = new ThreadLocalMap(this,firstValue);
可以看到很简单,只是新建一个ThreadLocalMap对象而已。
然后使用map.set(this, value);进行保存T对象,this当然就是ThreadLocal对象。
其实总结一下就是key为ThreadLocal对象,value是保存的对象,而每个线程内都拥有一个map,
每个map都拥有这个key,于是在不同的线程中,因为我们使用的map因线程而异,所以即使key没有变化,
也可以取到不同的T对象(就是每个线程的T对象其实是保存在自己线程内部的,通过ThreadLocal对象key找到而已)。
接下来就要说这里和弱引用有什么关系了。因为这个ThreadLocalMap并非用户定义,用户只是new了一个ThreadLocal对象,所以当用户定义的ThreadLocal对象不再使用之后,ThreadLocal对象及其指向的T对象都应该可以被回收。可是由于很多线程中的ThreadLocalMap对象中保存了ThreadLocal对象和T对象的Entry(键值对)中的ThreadLocal对象回收可能会受到阻碍。因此这里看代码可以看到ThreadLocal对象作为key的时候首先被WeakReference包裹了一下的,如下。
static class Entry extends WeakReference
Objectvalue;
Entry(ThreadLocal k, Object v)
super(k);
value = v;
可以看到这里的Entry类的k被做了弱引用,即ThreadLocal对象回收不会被这里的Entry所影响。效果就是在新的ThreadLocal执行set方法的时候,遍历map中的table时可能会发现好多Entry.get()为空的Entry,即key为空。这说明这条记录已经可以呗删掉了,这个时候就可以用新的Entry替换掉旧的。看一下代码吧。如下。
private void set(ThreadLocal key, Object value)
// Wedon't use a fast path as with get() because it is at
// leastas common to use set() to create new entries as
// it isto replace existing ones, in which case, a fast
// pathwould fail more often than not.
Entry[]tab = table;
int len =tab.length;
int i =key.threadLocalHashCode & (len-1);
for (Entrye = 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();
其中if(k == null)段就是做这个用的。如果这里不使用WeakReference则无法做到这点