ThreadLocal,很多地方叫做线程本地变量,也有些地方叫做线程本地存储。ThreadLocal为变量在每个线程中都创建了一个副本,那么每个线程可以访问自己内部的副本变量。
但是要注意,虽然ThreadLocal能够解决上面说的问题,但是由于在每个线程中都创建了副本,所以要考虑它对资源的消耗,比如内存的占用会比不使用ThreadLocal要大。
实现原理
拥有方法
下面看下几个怎么设计实现ThreadLocal的方法:
get
1 | /** |
- 获取当前线程;
- 然后通过getMap 获取Map;
- 获取到Map的键值对;
- 传入
this
当前ThreadLocal获取当前的键值对; - 根据获取到的entry 返回值,为null 的话调用
setInitialValue
方法;getMap
1
2
3
4
5
6
7
8
9
10/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
返回线程中的threadLocals变量,继续看threadLocals的实现;
threadLocals
1 | /* ThreadLocal values pertaining to this thread. This map is maintained |
它是ThreadLocal中的静态内部类ThreadLocalMap:
ThreadLocalMap
Entry
1 | static class Entry extends WeakReference<ThreadLocal<?>> { |
ThreadLocalMap的Entry继承了WeakReference,并且使用ThreadLocal作为键值。
setInitialValue
1 | /** |
如果map不为空,就设置键值对,为空,再创建Map,看一下createMap的实现;
createMap
1 | /** |
这样为当前线程创建副本变量就完毕了。
怎么创建副本变量
首先,在每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本(即T类型的变量)。
初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
使用场景
- 各种连接池获取连接(如,数据库连接,redis连接);
- session管理。
学习代码
1 | package com.wuwii.test; |
输出结果:
1 | 线程一的localValue为: Thread-0 |
可以看出,线程二并没有被影响。