1.是什么
只要线程处于活动状态且ThreadLocal实例可访问,每个线程都拥有对其线程本地变量副本的隐式引用; 一个线程消失后,它的所有线程局部实例副本都要进行垃圾收集(除非存在对这些副本的其他引用)
2.源码解读
2.1.给ThreadLocal设置值
ThreadLocal的set
方法,其实是首先获取当前线程,然后通过当前线程获取ThreadLocal的内部类ThreadLocalMap
,然后在设置ThreadLocalMap的K=this
(也就是设置K为ThreadLocal的实例),V=value
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);
}
}
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
}
2.2.从ThreadLocal获取值
首先获取当前线程Thread
,然后获取当前线程的缺省属性ThreadlocalMap
,然后查找ThreadLocalMap
中K为ThreadLocal实例的Entry
,最后返回value。如果ThreadLocalMap
为null或者Entry
不存在,则通过protected T initialValue()
方法来设置初始值,并进行set
方法操作。
protected T initialValue():该方法可以在实现时进行重写,这样可以在ThreadLocal的get方法没有获取到值时,返回initialValue()方法中的返回值
public class ThreadLocal<T> {
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap 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();
}
protected T initialValue() {
return null;
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
}
2.3.从ThreadLocal中移除值
获取当前线程的缺省属性ThreadLocalMap
,然后移除ThreadLocalMap
中K为ThreadLocal实例的Entry
public class ThreadLocal<T> {
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
}
2.4.ThreadLocal的内存泄漏问题
// static class ThreadLocal.ThreadLocalMap.Entry
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
通过源码,可以分析出Entry
的key是一个弱引用(如果一个对象只有弱引用指向它时,则这个对象会在虚拟机进行垃圾回收时被回收),所以若没有对象进行强引用到ThreadLocal
实例时,则Entry中的key会被垃圾回收变为null,造成当前线程的ThreadLocalMap
强引用Entry,但是无法获取到value的值,造成内存泄漏。所以JDK建议将ThreadLocal
设置为private static属性,这样避免被垃圾收回。引用关系如下图所示:
实际使用demo
package com.zkane.test;
/**
* @author 594781919@qq.com
* @date 2018/8/22
*/
public class ThreadLocalTest {
private static ThreadLocal<Long> threadLocalLong = new ThreadLocal(){
@Override
protected Long initialValue() {
return 11L;
}
};
private static ThreadLocal<String> threadLocalString = new ThreadLocal<>();
public static void main(String[] args) {
System.out.println("==" + threadLocalLong.get());
threadLocalLong.set(Thread.currentThread().getId());
threadLocalString.set(Thread.currentThread().getName());
new Thread(() -> {
threadLocalLong.set(Thread.currentThread().getId());
threadLocalString.set(Thread.currentThread().getName());
System.out.println("--->" + threadLocalLong);
System.out.println("--->" + threadLocalLong.get());
System.out.println("--->" + threadLocalString);
System.out.println("--->" + threadLocalString.get());
}).start();
Thread.yield();
System.out.println(threadLocalLong);
System.out.println(threadLocalLong.get());
System.out.println(threadLocalString);
System.out.println(threadLocalString.get());
}
}