ThreadLocal和WeakReference

Posted 好好学习312

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了ThreadLocal和WeakReference相关的知识,希望对你有一定的参考价值。

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则无法做到这点

以上是关于ThreadLocal和WeakReference的主要内容,如果未能解决你的问题,请参考以下文章

ThreadLocalMap里Entry为何声明为WeakReference?

ThreadLocal源码解析

ThreadLocal

ThreadLocal 内存泄露的根本原因

Java中的SoftReference和WeakReference有啥区别?

理解Java中的弱引用