文章目录
- ThreadLocal
- 源码分析:
- set方法
- get方法
- remove方法
- ThreadLocal内存泄漏问题
ThreadLocal
ThreadLocal提供了线程局部变量,每个线程都可以通过set和get方法来对这个变量进行操作,但不会和其他线程的局部变量冲突,实现了线程的数据隔离
源码分析:
set方法
public void set(T value) {Thread t = Thread.currentThread();//getMap就是为了获取当前线程的ThreadLocalMapThreadLocalMap map = getMap(t);if (map != null)//如果map存在就直接以这个ThreadLocal为键,设置键和值map.set(this, value);else//否则就为他创建一个ThreadLocalMap,并设置第一个键和值createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}
get方法
public T get() {Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null) {//如果当前线程的ThreadLocalMap存在,就尝试获取对应键值对ThreadLocalMap.Entry e = map.getEntry(this);if (e != null) {@SuppressWarnings("unchecked")T result = (T)e.value;return result;}}//如果threadLocals不存在或者当前的ThreadLocal不存在于这个map中return setInitialValue();
}
private T setInitialValue() {T value = initialValue();Thread t = Thread.currentThread();ThreadLocalMap map = getMap(t);if (map != null)//当前ThreadLocal不存在于map中,就加上,设置value为空map.set(this, value);else//如果map不存在就创建,并设置键值createMap(t, value);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.get() == key) {//弱引用中的清除方法e.clear();//将key为null的键值对清除掉,get和set方法底层也有用到expungeStaleEntry(i);return;}}
}
private static int nextIndex(int i, int len) {return ((i + 1 < len) ? i + 1 : 0);
}
总结:
可以看出,ThreadLocal中并不存储值,只是作为一个key来让线程从ThreadLocalMap中获取value,从而实现了线程之间的数据隔离
- 每个线程都维护着一个ThreadLocalMap,ThreadLocalMap是ThreadLocal的内部类
- ThreadLocal中的set,实际上是向当前线程的ThreadLocalMap中设置值,键为创建的ThreadLocal对象
ThreadLocal内存泄漏问题
内存泄漏:程序中间动态分配了内存,但在程序结束时没有释放这部分内存,从而造成那部分内存不可用的情况
内存溢出:要求分配的内存超过了系统能给的
ThreadLocal中作为map中的key使用,而且ThreadLocalMap中的key是弱引用,弱引用对象在gc时会被回收,而ThreadLocalMap和Thread的生命周期一样长,就会存在key为null的情况,value访问不到,从而引发内存泄漏。所以,使用ThreadLocal时最后最好调用remove方法显式调用expungeStaleEntry方法手动删除key为null的value,防止value的积累
ThreadLocal的get和set方法某些时候也会调用expungeStaleEntry方法,但这是不及时的,而且不一定每次都会执行