本文共 1666 字,大约阅读时间需要 5 分钟。
相信熟悉多线程的小伙伴,都熟悉这个对象的吧,今天咱们就来看一下他的内部构造以及和Thread的关系吧.
一切从set开始…public void set(T value) { Thread t = Thread.currentThread();//获取当前线程的id ThreadLocalMap map = getMap(t);//从线程中获取当前的map if (map != null) map.set(this, value); else createMap(t, value); }
看一下getMap(t)操作:
ThreadLocalMap getMap(Thread t) { return t.threadLocals;//着重说明一下,这个threadLocals对象放在Thread对象中的 }
看一下这个threadLocals变量对应类型的数据结构:
/** * The table, resized as necessary. * table.length MUST always be a power of two. */ private Entry[] table;//ThreadLocalMap基于一个数组实现的类似hashmap的实现
看一下Entry的结构:
static class Entry extends WeakReference> {//继承了弱引用,在每次垃圾回收时,如果判定为弱可达,则进行垃圾回收 /** The value associated with this ThreadLocal. */ Object value; Entry(ThreadLocal k, Object v) { super(k);//着重点出,弱引用回收对象是ThreadLocal对象 value = v; } }
再详细说明一下:
ThreadLocal tl = new ThreadLocal();tl.set("测试值");
在当前Thread对象的ThreadLocalMap中是这么存储的key=tl,value=“测试值”,也就是tl = “测试值”
基本的结构说完了,再说一下ThreadLocal内存泄漏的问题:
一般来说内存泄漏时因为ThreadLocal对象包装成弱引用,在下一次gc时已经进行垃圾回收,那么value呢??还是一直存在的…这就造成了泄漏了.一般来说只要value不是很大,这点泄漏不会导致什么大的问题,并且当线程停止后,也会进行回收.可是咱们很多时候使用ThreadLocal是这么用的
static ThreadLocal tl = newThreadLocal();
像这种情况,弱引用就失效了,静态关键字延长了tl的生命周期.如果是普通使用问题不会很大,如果是线程池配合该tl使用,会导致很多问题.
1.可能导致从tl中获取的结果不是想要的 2.如果tl对应的val是复杂类型,如map,当任务每次放入的key不同时,因为一直得不到释放,很容易造成不断累积,造成内存泄漏,这种情况比较严重;建议:
在使用线程池技术时,如果确实用到了复杂类型,并且每次可能放入不容的key,那么尽量复写protected void beforeExecute(Thread t, Runnable r) { }//或者protected void afterExecute(Runnable r, Throwable t) { }
这两个方法中任意一个,在其中执行tl.remove()来清理任务本地存储.
如有不足,请留言指正.
转载地址:http://mbuti.baihongyu.com/