一、代码使用示例
public class ThreadLocalDemo {
static ThreadLocal<String> localVar = new ThreadLocal<>();
static void print(String str) {
//打印--当前线程--中本地内存中本地变量的值
System.out.println(str + localVar.get());
//清除本地内存中的--当前线程--本地变量
localVar.remove();
}
public static void main(String[] args) {
Thread t1 = new Thread(new Runnable() {
@Override
public void run() {
//设置线程1中本地变量的值
localVar.set("线程1");
int a = 1;
//调用打印方法
print("thread t1: ");
//打印本地变量 print里面已经remove
System.out.println("删除后 : " + localVar.get());
}
});
Thread t2 = new Thread(new Runnable() {
@Override
public void run() {
//设置线程1中本地变量的值
localVar.set("线程2");
//调用打印方法
print("thread t2: ");
//打印本地变量 print里面已经remove
System.out.println("删除后 : " + localVar.get());
}
});
t1.start();
t2.start();
}
}
二、ThreadLocal源码分析
ThreadLocal的set方法,实际是往实际调用的当前线程Thread的 ThreadLocalMap 类型的变量threadLocals里面 set key为 当前 ThreadLocal,value为当前set的值。
ThreadLocal源码分析:
public void set(T value) {
//取当前实际调用的线程
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
//如果map不为null,开始set值,key为当前定义的ThreadLocal变量的this引用,值为添加的本地变量值
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
//返回Thread类下的定义的 ThreadLocal.ThreadLocalMap threadLocals
return t.threadLocals;
}
ThreadLocal类get方法,类似Map的方式,源码如下:
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();
}
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;
}
三、父子线程间变量传递问题
同一个ThreadLocal变量在父线程中被设置值后,在子线程中是获取不到的。因为threadLocals为当前调用线程对应的本地变量,所以二者是不能共享的。
那子线程如何访问父线程的本地变量呢,可以使用InheritableThreadLocal类。InheritableThreadLocal类继承了ThreadLocal类,并重写了childValue、getMap、createMap三个方法。
使用示例:
public class InheritableThreadLocalDemo {
private static InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
threadLocal.set("主线程变量");
System.out.println("value main:"+threadLocal.get());
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String value = threadLocal.get();
System.out.println("value child:"+value);
}
});
thread.start();
}
}
输出结果:
value main: 主线程变量
value child: 主线程变量
四、注意事项
ThreadLocalMap中的Entry的key使用的是ThreadLocal对象的弱引用,在没有其他地方对ThreadLocal依赖,ThreadLocalMap中的ThreadLocal对象就会被回收掉,但是对应的不会被回收,这个时候Map中就可能存在key为null但是value不为null的项,所以,在实际的时候使用完毕后要及时调用remove方法避免内存泄漏。