第一次见到这个东西,还是写项目中遇见的,了解一下。
在Java多线程模块中,ThreadLocal
是经常被提问到的一个只是点,因此只有理解透彻了,不管怎么问,都能游刃有余。
1. ThreadLocal是什么
从名字上我们就可以看出叫做本地线程变量,意思是说,ThreadLocal
中填充地是当前线程的变量,该变量对其他线程而言是封闭且隔离地,ThreadLocal
为变量在每个线程中创建了一个副本,这样每个线程都可以访问自己内部地副本变量。
从字面意思很容易理解,但是实际角度就没那么容易了,作为一个面试常问的点,使用场景也是很丰富。
- 在进行对象跨层传递的时候,使用
ThreadLocal
可以避免多次传递,打破层级间的约束。 - 线程间数据隔离
- 进行事务操作,用于存储线程事务信息。
- 数据库连接,
Session
会话管理
2.ThreadLocal怎么用
我们下来看一个例子:
public class demo01 {
public static void main(String[] args) {
ThreadLocal<String> local = new ThreadLocal<>();
IntStream.range(0, 10).forEach(i -> new Thread(() -> {
local.set(Thread.currentThread().getName() + ":==>" + i);
System.out.println("线程: " + Thread.currentThread().getName() + ",local: " + local.get());
}).start());
}
}
结果
线程: Thread-0,local: Thread-0:==>0
线程: Thread-3,local: Thread-3:==>3
线程: Thread-4,local: Thread-4:==>4
线程: Thread-2,local: Thread-2:==>2
线程: Thread-1,local: Thread-1:==>1
线程: Thread-8,local: Thread-8:==>8
线程: Thread-9,local: Thread-9:==>9
线程: Thread-5,local: Thread-5:==>5
线程: Thread-7,local: Thread-7:==>7
线程: Thread-6,local: Thread-6:==>6
进程已结束,退出代码为 0
从结果可以看出,每一个线程都有自己的local
值,这就是ThreadLocal
的基本使用。
3.ThreadLocal源码分析
3.1 set方法
下面我们看下源码,了解其工作原理。
public void set(T value) {
// 首先获取当前线程对象
Thread t = Thread.currentThread();
// 获取线程中变量 ThreadLocal.ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 如果不为空,表示当前线程之前已经创建过了,现在重新赋值
if (map != null) {
map.set(this, value);
} else {
// 如果为空,初始化该线程对象的map变量,其中key为当前的threadlocal变量
createMap(t, value);
}
}
// 获取当先线程的ThreadLocalMap对象
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
// ThreadLocal.ThreadLocalMap threadLocals = null;
// threadLocals就是ThreadLocalMap
void createMap(Thread t, T firstValue) {
// 初始化线程内部变量threadlocals,
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
汇总下,ThreadLocalMap
为 ThreadLocal
的一个静态内部类,里面定义了Entry
来保存数据。而且是继承的弱引用。在Entry
内部使用ThreadLocal
作为key
,使用我们设置的value
作为value
。
对于每个线程内部有个ThreadLocal.ThreadLocalMap
变量,存取值的时候,也是从这个容器中来获取。
3.1 get方法
public T get() {
// 获取当前线程对象
Thread t = Thread.currentThread();
// 获取当前线程的ThreadLocalMap对象
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();
}
通过上面的分析,相信你对该方法已经有所理解了,首先获取当前线程,然后通过key threadlocal
获取 设置的value
。
3.3 remove方法
public void remove() {
//获取当前线程绑定的threadLocals
ThreadLocalMap m = getMap(Thread.currentThread());
//如果map不为null,就移除当前线程中指定ThreadLocal实例的本地变量
if (m != null) {
m.remove(this);
}
}