ThreadLocal叫做线程本地变量,它可以存储一些只有该线程使用的变量,而线程可以安全得访问ThreadLocal中存储的内容。
ThreadLocal的相关结构
ThreadLocalMap
ThreadLocal中有一个静态内部类ThreadLocalMap,ThreadLocalMap用于存储具体的变量内容。ThreadLocalMap是一个Map结构,key为ThreadLocal,值是Entry变量,且该变量是一个弱引用。
ThreadLocalMap部分定义如下:
static class ThreadLocalMap {
/**
* Entry继承弱引用WeakReference
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/**
* value值
*/
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// 默认数组长度
private static final int INITIAL_CAPACITY = 16;
// 数组
private Entry[] table;
// 从ThreadLocalMap中获取本地变量值,key为ThreadLocal
private ThreadLocal.ThreadLocalMap.Entry getEntry(ThreadLocal<?> key) {
//...
}
// 向ThreadLocalMap中设置本地变量值,key为ThreadLocal
private void set(ThreadLocal<?> key, Object value) {
//...
}
}
ThreadLocal
ThreadLocal提供访问,设置,删除本地变量的方法,实质上是对ThreadLoalMap的操作。
ThreadLocal的方法如下:
- set:为当前线程设置变量,当前ThreadLocal作为索引
- get:获取当前线程变量,当前ThreadLocal作为索引
- initialValue(钩子方法需要子类实现):懒加载形式初始化线程本地变量,执行get时,发现线程本地变量为null,就会执行initialValue的内容
- remove:清空当前线程的ThreadLocal索引与映射的元素
Thread
Thread类中声明了成员变量threadLocals(ThreadLocal.ThreadLocalMap threadLocals = null;),threadLcoals是ThreadLocalMap的一个对象,可以存储线程本地变量。由于每个Thread都有自己的线程本地变量,所以线程本地变量拥有线程隔离性,也就是线程可以安全得访问。
一个Thread可以有多个ThreadLocal用于存储不同类型的本地变量,这些变量都存储在ThreadLocalMap中,其中ThreadLocal为key,Entry包装的变量为value,并且可以通过ThreadLocal提供的方法访问,设置,删除这些本地变量。
ThreadLocal提供的方法
一个ThreadLocal使用的实例
public class UserContext {
ThreadLocal<String> threadLocal = new ThreadLocal(){
@Override
protected String initialValue(){
return "User";
}
};
public String getUserInfo(){
return threadLocal.get();
}
public void setUserInfo(String userInfo){
threadLocal.set(userInfo);
}
public void removeUserInfo(){
threadLocal.remove();
}
}
getMap方法
getMap()方法用于获取该线程threadLocals变量,即实际上存储本地变量的ThreadlLocalMap。
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
get方法
get()方法用于获取当前线程存储的本地变量。
public T get() {
// 当前线程
Thread t = Thread.currentThread();
// 线程的threadLocals变量
ThreadLocalMap map = getMap(t);
if (map != null) {
// 当前ThreadLocal为key,在map中获取Entry变量
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// 如果没有获取到对应的值,执行initialValue()方法
return setInitialValue();
}
set方法
set()方法用于设置当前线程存储的本地变量
public void set(T value) {
// 当前线程
Thread t = Thread.currentThread();
// 线程的threadLocals变量
ThreadLocalMap map = getMap(t);
if (map != null)
// map不为空,直接添加值,当前ThreadLocal为key
map.set(this, value);
else
// map为空,先创建ThreadLocalMap,再设置值
createMap(t, value);
}
remove方法
remove()方法用于清除本地变量。
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
通过在业务中,一个线程的生命周期很长,一个线程处理完第一个任务之后会再去处理第二个任务,若不及时清理本地存储的变量,则上一个任务用到的变量对被带到下一个任务中使用,很可能导致意想不到的结果。
remove使用场景:
- 生命周期较长的线程场景
- 无限循环线程的场景
- 线程池场景