ThreadLocal原理
Thread类里维护了一个ThreadLocalMap,这个map的key是ThreadLocal,所有说一个Thread里可以有多个ThreadLocal
-
ThreadLocal作用:
提供线程内的局部变量,不同的线程之间不会相互干扰,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或组件之间一些公共变量传递的复杂度
-
常用方法:
- ThreadLocal():构造方法
- public void set(T value):给当前线程绑定局部变量
- public T get():取出当前线程绑定的局部变量
- public void remove():删除当前线程绑定的局部变量
-
ThreadLocal与synchronized的区别:
- ThreadLocal相当于是空间换时间,为每一个线程提供一份变量的副本,从而实现同时访问而相互不干扰
- synchronized则是时间换空间,只有一份变量,让不同线程排队访问
- 时间带来的是性能的损耗,并发性能的降低(一个一个等着取东西)
-
ThreadLocal内部设计:
-
看源码:
-
set():
public void set(T value) { //通过当前线程来取ThreadLocalMap Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); //判断map是否存在 if (map != null) { map.set(this, value); } else { createMap(t, value); } } ThreadLocalMap getMap(Thread t) { return t.threadLocals;//这个threadLocals就是维护的ThreadLocalMap } void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue); }
-
get():
public T get() { //通过当前线程来取ThreadLocalMap Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } //没有map 或 有map没取到value return setInitialValue(); } private T setInitialValue() { T value = initialValue();//默认是给空值,可重写 Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); if (map != null) { map.set(this, value); } else { createMap(t, value); } if (this instanceof TerminatingThreadLocal) { TerminatingThreadLocal.register((TerminatingThreadLocal<?>) this); } return value; } protected T initialValue() { return null; }
-
remove():
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) { m.remove(this); } } private void remove(ThreadLocal<?> key) { 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)]) { if (e.refersTo(key)) { e.clear(); expungeStaleEntry(i); return; } } }
-
ThreadLocalMap(是Thread的一个属性):
key是当前ThreadLoal对象的引用
value是Entry:
static class Entry extends WeakReference<ThreadLocal<?>> { //Entry继承了弱引用,本身也是个键值对 Object value; Entry(ThreadLocal<?> k, Object v) { super(k); value = v; } }
-
-
弱引用相关:
-
内存溢出:就是内存不够用了
-
内存泄漏:已分配出去的内存,因为某些原因无法GC了,那就GG了
-
弱引用:垃圾回收器一旦发现具有弱引用的对象,不管当前内存空间足够与否,都会进行GC
-
-
使用完ThreadLoacl,ThreadLoacl ref被回收,key又是弱引用,所以ThreadLoacl可以被GC
-
但是如果没有手动删除这个Entry以及CurrentThread还在运行的前提下,存在强引用链Thread Ref -> current thread-> ThreadLoaclMap -> Entry -> value,value不会被GC,且此时ThreadLoacl已经被GC,key为null,意味着value无法被访问,value这块内存就是泄露了
-
内存泄漏的原因是:
- 使用完删除Entry
- CurrentThread依然运行
-
内存泄漏的根源:
ThreadLocalMap是Thread的一个属性,所以两者的生命周期一样长,如果没有手动删除Entry就会内存泄漏
-
但是但是:
- ThreadLocalMap内部的set/get/remove方法中,会对key(ThreadLocal)为null的情况进行判断,如果key为null,会对value赋值为null,这意味着就算你使用完没删除Entry,在你下一次调用set/get/remove方法时,会清楚value,避免内存泄漏,这是一种保障,也是使用弱引用的优势
-
-
Hash冲突:
ThreadLocalMap使用线性探测法来解决Hash冲突:
一次探测下一个地址,直到空的地址插入,若整个空间都找不到空余的地址,就产生溢出
可以把Entry[ ] table看成是一个环形数组
value,避免内存泄漏,这是一种保障,也是使用弱引用的优势 -
Hash冲突:
ThreadLocalMap使用线性探测法来解决Hash冲突:
一次探测下一个地址,直到空的地址插入,若整个空间都找不到空余的地址,就产生溢出
可以把Entry[ ] table看成是一个环形数组