ThreadLocal和WeakReference

本文深入解析了ThreadLocal的工作原理,包括其如何实现线程隔离、数据存储方式以及弱引用的应用来避免内存泄漏。
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则无法做到这点
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值