ThreadLocal
ThreadLocal是JDK包提供的,它提供了线程本地变量,访问这个变量的每一个线程都会有这个变量的一个本地副本。当多线程操作这个变量时,实际上是操作自己本地内存里的变量。
1、使用示例
package io.juc;
public class Juc_11 {
static void print(String str){
System.out.println(str+":"+localVariable.get());
localVariable.remove();
}
static ThreadLocal<String> localVariable=new ThreadLocal<>();
public static void main(String[] args) {
Thread threadOne =new Thread(new Runnable() {
@Override
public void run() {
//设置的是线程one本地内存中的一个副本,所以线程two无法访问
localVariable.set("threadOne local variable");
print("threadOne");
System.out.println("threadOne remove after:"+localVariable.get());
}
});
Thread threadTwo=new Thread(new Runnable() {
@Override
public void run() {
localVariable.set("threadTwo local variable");
print("threadTwo");
System.out.println("threadTwo remove after:"+localVariable.get());
}
});
threadOne.start();
threadTwo.start();
}
}
2、实现原理
类图如下
源码分析
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的threadLocals,见下一段代码
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();
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
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;
}
set方法
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
//没有就进行创建
createMap(t, value);
}
3、inheritableThreadLocals
子线程可以使用父线程的变量
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
//inheritableThreadLocals代替了
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
//在构造Thread时,init,会先检查父线程的inheritableThreadLocals是否为null,如果不是的话,会将当前的inheritThreadLocals通过构造parent.inheritableThreadLocals构造
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
4、应用场景
那么在什么情况下需要 线程可以获取父线程的 threadlocal 变量呢?情况还是蛮多
的,比如子线程需要使用存放在 threadlocal 中的用户 登录信息,再比如一些中间件
要把统一的 id 个调用链路记录下来。其实子线程使用父线程中的 threadlocal
法有多种方式, 比如创建线程时传入父线程中的变量,并将其复制到子线程中,或者在父
线程中构造一个 map 作为参数传递给子线程,但是这些都改变了我们的使用习惯,所以
在这些情况下 Inheritab I e ThreadLocal 就显得比较有用