含义
线程本地变量,线程内部进行数据传递,即每个线程对ThreadLocal的读写操作是线程隔离的,互不影响的
实现原理
- Thread 类内部有个 ThreadLocal.ThreadLocalMap 的变量,即每个线程都维护一个自己的ThreadLocalMap,目的就是实现线程隔离
public class Thread implements Runnable{
ThreadLocal.ThreadLocalMap threadLocals = null;
}
-
当调用ThreadLocal的set方法时,实际是向当前线程(Thread)类的ThreadLocalMap的中添加元素,其中ThreadLocalMap中的Entry是的key是Threadloacal的弱引用
public class ThreadLocal<T> {
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
map.set(this, value);
} else {
createMap(t, value);
}
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
}
static class Entry extends WeakReference<ThreadLocal<?>>{
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
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;
}
}
//设置key,value放入map中
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
- 同样读也是从当前线程中读取
为什么使用弱引用
- 首先需明白强引用,软引用,弱引用,虚引用的
- 弱引用主要是防止内存泄漏,如果没有弱引用,只要线程还存在,即使程序没有了对ThreadLocal的强引用,当时线程内部的 ThreadLocalMap 引用依旧存在,那么key的强引用也依旧存在,那么key就无法被回收,如此就会造成key的内存泄漏,进而是 value的泄漏,但是如果key是弱引用,那么当垃圾回收时,只要ThreadLocal强引用没有了,key就可以被回收,这样就不会造成内存泄漏了。
父子线程
- InheritableThreadLocal提供一种父子线程之间数据共享功能
- 具体实现是在Thread类中除了threadLocals外还有一个
inheritableThreadLocals
对象。在线程对象初始化的时候,会调用ThreadLocal的createInheritedMap
从父线程的inheritableThreadLocals
中把有效的entry拷过来
private Thread(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
//省略...
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
//...
}
- 需要注意的是
InheritableThreadLocal
只是在子线程创建的时候会去拷一份父线程的inheritableThreadLocals
。如果父线程是在子线程创建后再set某个InheritableThreadLocal对象的值,对子线程是不可见的。
应用场景
注意事项:
- 使用完 ThreadLocal ,最好手动调用 remove() 方法,否则可能出现内存泄漏问题(原因就是ThreadLocalMap的key 是ThreadLocal 的弱引用,若key 被回收了,但是value却可以一直存在,对应null的key,这样value就无法被回收了造成内存泄漏)