线程安全问题
Thread代码如下,ThreadLocal.ThreadLocalMap threadLocals 这个为 Thread的属性
1 | public |
下面是ThreadLocal的set方法,每次在set的时候都会取出对应的线程的threadLocals属性,所以这里不存在线程并发的问题, ThreadLocal.ThreadLocalMap中维护了一个类似于hashmap的数组链表结构,注意这里的map存放的是当前线程下的多个ThreadLocal,并不是多个线程的ThreadLocal,这里一定要搞清楚.
1 | public void set(T value) { |
内存泄漏问题
ThreadLocalMap使用ThreadLocal的弱引用作为key,如果一个ThreadLocal没有外部强引用来引用它,那么系统 GC 的时候,这个ThreadLocal势必会被回收,这样一来,ThreadLocalMap中就会出现key为null的Entry,就没有办法访问这些key为null的Entry的value,如果当前线程再迟迟不结束的话,这些key为null的Entry的value就会一直存在一条强引用链:Thread Ref -> Thread -> ThreaLocalMap -> Entry -> value永远无法回收,造成内存泄漏。
set(null)是把ThreadLocalMap的value赋值为null,并没有删掉这个K-V这个元素
而remove则是把K-V这个元素都删掉了,两个不同的东西,不要弄混了
1 | public void remove() { |
如下是遍历ThreadLocalMap中的key做remove操作,后续还有对应的rehash等操作,如果是set(null)则只是把value置成null,但是该key还存在,所以属于内存泄漏
1 | private void remove(ThreadLocal<?> key) { |
弱引用
如下所示, ThreadLocalMap中的key(ThreadLocal)为弱引用,当该ThreadLocal没有被强引用时,如果出现了GC则会回收该key,使得key变为null,其实,ThreadLocalMap的设计中已经考虑到这种情况,也加上了一些防护措施:在ThreadLocal的get(),set(),remove()的时候都会清除线程ThreadLocalMap里所有key为null的value。
1 | static class ThreadLocalMap { |