ThreadLocal的简单使用
ThreadLocal
在线程内部调用set(value)
方法存储值。
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
在set方法内部,我们获取到当前线程对象,通过ThreadLocal
成员方法getMap(t)
根据当前线程对象获取到当前线程的的成员属性变量threadLocals
(变量类型是ThreadLocalMap
,在createMap(t,value)
中初始化)这是key-value存储容器,我们调用这个threadLocals
来实现保存和修改数据的操作。保存数据有两种方式,一个通过ThreadLocalMap
的成员方法set(this, value)
,通过ThreadLocal
成员方法 createMap(t, value)
实现。
ThreadLocal
的成员方法createMap(线程对象,value)
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
在createMap
内部我们直接调用ThreadLocalMap
的构造函数传入当前ThreadLocal
对象和要保存的数据值创建该对象,并把该对象的引用传递给当前线程的属性变量threadLocals
。
ThreadLocalMap
的构造函数
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(threadLocal,firstValue)
构造函数内部,我们初始化了一个Entry
对象(Entry
是ThreadLocalMap
的静态内部类,继承了WeakReference
对象),这个对象存储了传入的ThreadLocal对象和数据对象。我们把这个对象加入一个初始化长度为16的Entry
类型的数组表中。
ThreadLocalMap
的成员方法set(this, value)
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
if (k == key) {
e.value = value;
return;
}
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
在set(threadLocal, value)
的方法内部,我们会先遍历table表判断是否存在存储了该ThreadLocal对象的entry存在,如果存在则替换数据值,不存在则新建entry对象,加入到table表中。
ThreadLocal
在线程内部调用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方法内部,我们首先获取到当前线程对象,再通过当前线程对象获取到它的属性变量threadLocals
,调用它的getEntry(threadLocal)
获取到Entry
对象,通过Entry对象我们可以直接获取到值。
- 总结
ThreadLocal为每一个线程都单独维护了一个ThreadLocalMap对象,其中一个线程通过ThreadLocal存储或者修改他的数据对象时,不会影响到其他线程。