ThreadLocal的构造方法
public ThreadLocal() {
}
set
public void set(T value) {
Thread t = Thread.currentThread();//当前线程
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);//this==》当前threadLocal对象;
else
createMap(t, value);
}
为什么ThreadLocal能够保存各个线程的私有变量?
因为ThreadLocal只是相当于一个工具key,其实值是存储在当前线程中的;在上面的set方法中,首先是获取当前线程 t , 然后利用getMap(t) ,从当前线程t中,获取到ThreadLocalMap对象 threadLocals;然后将ThreadLocal对象和value值以key-val形式保存在ThreadLocalMap 的Entry中;
get
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();
}
get过程:
- 1.获取到当前线程
- 2.通过当前线程,获取到ThreadLocalMap 的对象:threadLocals
- 3.从threadLocals对象中获取ThreadLocal对象,找到值:value;
ThreadLocalMap
set值的时候,实际上是真正存值的是ThreadLocalMap 对象;
我们可以看下ThreadLocalMap 源码:
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/** 初始化容量大小*/
private static final int INITIAL_CAPACITY = 16;
/** 储存entry的table,和之前的HashMap中的entry不同*/
private Entry[] table;
/** 已经存储的entry数量*/
private int size = 0;
/** table扩容的阈值*/
private int threshold;
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);
}
ThreadLocalMap 的结构中有2个很关键的结构:
- Entry对象,保存ThreadLocal对象和变量值:value
- table数组,存储的是Entry对象;
ThreadLocalMap 虽然叫map,但和常见的HashMap不一样,它的结构不是:数组+链表/数组+链表+红黑树;
它仅仅只是一个table数组;数组存储的对象是Entry,Entry是一个key-value的结构;
测试
我们知道ThreadLocal存值的ThreadLocalMap是一个数组,因此一个线程可以添加多个ThreadLocal对象;
ThreadLocal<String> threadLocal = new ThreadLocal<>();
ThreadLocal<String> threadLocal2 = new ThreadLocal<>();
Thread t1 = new Thread(()->{
threadLocal.set("1");
threadLocal2.set("threadLocal2 var2. ,,,,,,")
System.out.println("t1:"+threadLocal.get()+";threadLocal2 :"+threadLocal2 .get());});
Thread t2 = new Thread(()->{threadLocal.set("2");
System.out.println("t2:"+threadLocal.get());});
Thread t3 = new Thread(()->{threadLocal.set("3");
System.out.println("t3:"+threadLocal.get());});
t1.start();
t2.start();
t3.start();
每个线程都可以根据自身情况,添加ThreadLocal变量;如果有2个变量需要个线程单独存储,可以添加2个ThreadLocal对象;也可以将2个变量打包成一个类,只需要用一个ThreadLocal来存储都是可以的;
比如浏览器请求服务器时,每一个请求对应一个request对象和一个response对象;就有2种方式来存储
方式一:
ThreadLocal<Request > request = new ThreadLocal<>();
ThreadLocal<Response > response = new ThreadLocal<>();
方式二:打包成session类
public class Session{
Request request ;
Response response ;
}
ThreadLocal<Session> session= new ThreadLocal<>();