Threadlocal源码分析以及其中WeakReference做用分析

今天在看Spring 3.x企业应用开发实战,第九章 Spring的事务管理,9.2.2节ThreadLocal的接口方法时,书上有提到Threadlocal的简单实现,我就去看了下JDK1.8的Threadlocal的源码。发现实现方式与书中讲的并不相同,同时在网上搜索了一下,发现有比较多的人理解错了。java

先看一下容易误导的解释:在ThreadLocal类中有一个Map对象,这个Map以每一个Thread对象为键,保存了这个线程对应局部变量值,对应的实现方式以下:数组

 
 
 
 
public class SimpleThreadLocal { private Map valueMap = Collections.synchronizedMap(new HashMap()); public void set(Object newValue) { valueMap.put(Thread.currentThread(), newValue);//①键为线程对象,值为本线程的变量副本 } public Object get() { Thread currentThread = Thread.currentThread(); Object o = valueMap.get(currentThread);//②返回本线程对应的变量 if (o == null && !valueMap.containsKey(currentThread)) {//③若是在Map中不存在,放到Map 中保存起来。 o = initialValue(); valueMap.put(currentThread, o); } return o; } public void remove() { valueMap.remove(Thread.currentThread()); } public Object initialValue() { return null; }}

为何不按照那种有误的方法实现呢?
看起来彷佛是不一样线程获取了各自的值,可是这些值并无线程独立。线程A能够操做线程B对应的值。若是某个线程将保存这些值的Map置为null了,那么其余线程也没法访问了。this

其实是怎样的呢
咱们看ThreadLocal的get()方法源码。能够看到获取Threadlocal中的值,是先经过当前线程的线程对象t,获取t的ThreadlocalMap属性对象,而后再以Threadlocal对象为键,去获取ThreadlocalMap中的值。spa

 
 
 
 
public T get() { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t);//获取线程对象的属性 if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this);//这里的this是指Threadlocal对象,Threadlocal在类中一般以静态属性出现,因此多个线程的Threadlocal指向同一个对象。 if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue(); }ThreadLocalMap getMap(Thread t) { return t.threadLocals;}

同时咱们查看ThreadLocal源码中定义的静态类ThreadLocalMap,其实底层封装的是一个Entry数组,获取方式和普通的HashMap不太同样,若是没有命中,就直接经过线性搜索,由于ThreadLocalMap须要保存的Entry并不会太多。线程

 
 
 
 
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); }private void set(ThreadLocal<?> key, Object value) { // We don't use a fast path as with get() because it is at // least as common to use set() to create new entries as // it is to replace existing ones, in which case, a fast // path would fail more often than not. 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; } }

经过ThreadLocal,每一个线程保存自身的数据,不能访问到其余线程的数据。code

ThreadLocalMap的Entry使用ThreadLocal的WeakReference引用做为Key值,当全部线程运行出ThreadLocal的做用域时,即没有强引用ThreadLocal时,ThreadLocal就会被回收。对象

 
 
 
 
static class Entry extends WeakReference<ThreadLocal<?>> { /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; }}

不是Threadlocal为每一个线程提供了独立的变量,而是每一个线程本身带了本身独立的变量。接口

关于内存泄漏
关于ThreadLocalMap的内存泄漏:若是一个ThreadLocal的生命周期结束,即在ThreadLocal所处的类中没有了强引用,而Thread没有结束,在Thread的threadLocals成员变量中,会有一个Entry使用弱引用引用了ThreadLocal做为key,由于是弱引用,这个key将被回收。而value是强引用,看起来是会形成泄漏,可是在ThreadLocalMap的set和get方法中,有一些释放的方法。具体的我也不太懂。
仍是老老实实使用ThreadLocal的remove方法比较好。生命周期

 
 
 
 
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) m.remove(this);}


相关文章
相关标签/搜索