ThreadLocal详解
TheadLocal 被叫做线程局部变量或者线程局部存储,作用就是将多个线程共享的变量拷贝一份到当前线程中使用,实质上就是线程中的局部变量,只不过这个变量的作用域是线程的作用域。
ThreadLocal类中只有五个方法
修饰符和返回值类型 | 方法和描述 |
---|---|
public T | get()获取到当前线程中局部变量的值 |
protected T | initialValue()返回此线程局部变量的初始值 |
public void | remove()删除此线程局部变量的值 |
public void | set()设置当前线程局部变量的值 |
public static <S> ThreadLocal <S> | 创建一个SuppliedThreadLocal对象 |
public void set(T value)
源码:
public void set(T value) {
Thread t = Thread.currentThread(); //获取当前线程
ThreadLocalMap map = getMap(t); //获取到当前线程的ThreadLocalMap
if (map != null) //如果map不为空 证明已经当前线程已经有局部变量了
map.set(this, value); //将当前局部变脸存入map
else
createMap(t, value); //如果map为空证明第一次存值,则先创建map集合
}
getMap()方法
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; //在Thread类中有一个threadLocals属性存储的就是ThreadLocalMap
}
如果map集合为空则执行createMap方法
源码:
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue); // 创建一个ThreadLocalMap对象初始化并且返回
}
如果getMap获取到的map不为空 则调用ThreadLocalMap的set方法存值
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; //在creatMap时 创建了初始容量为16的Entry数组
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) { //如果k为null则证明键值被回收了内存泄漏
replaceStaleEntry(key, value, i);// 替换过期的键值
return;
}
}
//执行到这的条件是又创建了一个ThreadLocal对象然后存值 会形成条件k!=key k!=null 则会先执行i = nextIndex(i, len)
//然后e==null 直接执行下面的代码 直接创建一个Entry对象然后将值存入
// 直接存值
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
public T get()
源码:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t); //获取到当前线程对象的ThreadLocalMap集合
if (map != null) { //map不为空
ThreadLocalMap.Entry e = map.getEntry(this); //根据当前的ThreadLocal对象获取到存值的Entry对象e
if (e != null) { //e不为空
@SuppressWarnings("unchecked")
T result = (T)e.value; //直接取出value值
return result; //返回结果
}
}
return setInitialValue(); //如果map为空则给局部线程变量设置初始值
}
ThreadLocalMap中的getEntry方法
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)//如果e.get()返回的值与key不相等
//则证明查询的第二个线程局部变量 则执行getEntryAfterMiss方法
return e;
else
return getEntryAfterMiss(key, i, e);
}
ThreadLocalMap中的getEntryAfterMiss方法
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key) //如果校验成功是要查询的线程局部变量则返回Entry对象e
return e;
if (k == null)
expungeStaleEntry(i);
else //如果查询的是第二个线程局部变量则会执行到这 证明下标i 需要更新
i = nextIndex(i, len);
e = tab[i]; //使用更新后的下标获取到新的Entry
}
return null;
}
setInitialValue方法
该方法和ThreadLocal的set方法几乎一样,只是多个获取初始值这个步骤
private T setInitialValue() {
T value = initialValue(); //获取到初始化的值
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) //map不为空则调用map的set方法
map.set(this, value);
else
createMap(t, value); //创建map
return value; //返回初始的值
}
public void remove()
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread()); //获取到当前线程对应的Map集合
if (m != null) //不为空则删除数据
m.remove(this); //执行Map集合的remove方法
}
ThreadLocalMap的remove方法
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(); //调用Entry中的clear方法
expungeStaleEntry(i);
return;
}
}
}
Entry是ThreadLocalMap的静态内部类 ThreadLocalMap又是ThreadLocal的静态内部类
//很明显Entyr继承了WeakReference<ThreadLocal<?>> 则证明Key被弱引用包装了
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k); //调用了父类Reference的构造方法
value = v;
}
}
Reference的构造方法(super(k);)
Reference(T referent) {
this(referent, null); //调用了两个参数的构造方法
}
Reference(T referent, ReferenceQueue<? super T> queue) {
this.referent = referent; //将键值k赋值给了属性referent
this.queue = (queue == null) ? ReferenceQueue.NULL : queue;
}
搞清楚了键值最终被如何包装之后,再回到Entry的clear方法(这个clear方法实质上是继承父类Reference的)
//e.clear()最终调用了 Reference中的clear方法
public void clear() {
this.referent = null; //上面说了键值最终被赋值给了 referent
//现在将referent置为null则实现了清楚功能,键值为null则对应了value无法找到也就实现了清除功能
}
protected T initialValue()
这个方法设计的就是为了 让子类去重写 如果不重写默认返回的初始值就是null
protected T initialValue() {
return null;
}