一句话定义
ThreadLocal通过每个线程独有的ThreadLocalMap来存储数据,以ThreadLocal自身为Key,实现线程隔离。
理解过程
ThreadLocal是用于存取当前线程中临时变量的一个工具,Thread中存在ThreadLocalMap的变量,当ThreadLocal设置变量时,先查出当前线程下的ThreadLocalMap,如果为null,则创建ThreadLocalMap,key是当前的ThreadLocal对象,value是要存入的值,再将构建好的ThreadLocalMap赋值给当前线程下的ThreadLocalMap。
解决什么问题
ThreadLocal为每个线程提供了独立的变量副本来实现线程安全。
隔离性:每个线程都有自己的ThreadLocalMap,map的value只对当前线程可见,其他线程无法访问。
独立性:即使多个线程操作同一个ThreadLocal实例,但是每个线程都有自己的ThreadLocalMap,只不过key是同一个,最终操作的还是自己的副本。
场景:在web应用中,ThreadLocal常用语存储当前线程的用户信息,确保每个请求处理自己的数据。
最重要的API/动作
get方法
public T get() { // 获取当前的线程Thread t = Thread.currentThread(); // 通过当前的线程获取ThreadLocalMapThreadLocalMap map = getMap(t); if (map != null) { ThreadLocalMap.Entry e = map.getEntry(this); if (e != null) { @SuppressWarnings("unchecked") T result = (T)e.value; return result; } } return setInitialValue();
}
set方法
public void set(T value) { Thread t = Thread.currentThread(); ThreadLocalMap map = getMap(t); // 如果不为空插入k vif (map != null) { map.set(this, value); } else { // 如果是空创建ThreadLocalMap,并插入k vcreateMap(t, value); }
}
看下getMap方法和createMap方法,帮助理解上述的代码
ThreadLocalMap getMap(Thread t) { return t.threadLocals;
}
void createMap(Thread t, T firstValue) { t.threadLocals = new ThreadLocalMap(this, firstValue);
}
remove()
public void remove() { ThreadLocalMap m = getMap(Thread.currentThread()); if (m != null) { m.remove(this); }
}
最大缺陷
当ThreadLocal使用完后被回收后,但是由于ThreadLocalMap是被线程持有,所以不会被回收,但是ThreadLocalMap中仍然存在null->value的引用,如果有长时间占用可能会发生内存泄漏。所以当使用完后手动remove确保资源释放。
remove方法会调用ThreadLocalMap的remove方法中的expungeStaleEntries方法,它会遍历删除key为null的entry。