关系
ThreadLocalMap是ThreadLocal的静态内部类,它也是Thread的成员变量
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
看一下demo
public void start(){
ThreadLocal<Boolean> threadLocal = new ThreadLocal<>();
threadLocal.set(true);
Log.d(TAG, Thread.currentThread().getName() + " 的值是 " + threadLocal.get());
**线程1**
new Thread(new Runnable() {
@Override
public void run() {
threadLocal.set(false);
Log.d(TAG, Thread.currentThread().getName() + " bool的值是 " + threadLocal.get());
ThreadLocal<Integer> dd = new ThreadLocal<>();
dd.set(55);
Log.d(TAG, Thread.currentThread().getName() + " int的值是 " + dd.get());
}
}).start();
**线程2**
new Thread(new Runnable() {
@Override
public void run() {
Log.d(TAG, Thread.currentThread().getName() + " 的值是 " + threadLocal.get());
}
}).start();
在主线程定义一个threadLocal变量
- 在主线程中设置true,
- 在子线程1中设置false,然后又new一个新的对象,设置55
- 在子线程2中没有操作,直接获取 查看打印的结果
分析
1. ThreadLocal的set方法
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
以子线程1为例,那么首先获取子线程1,然后调用getMap(t)方法,
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
因为我们创建线程的时候并没有初始化threadLocals,所以返回null,那么根据上面的判断,
- 如果返回null,则会调用
createMap(t, value)
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
这里分2步
- 创建ThreadLocalMap对象
- 把对象指给当前线程的threadLocals,那么下次就不用再new了,这里采用的是懒加载的方式。 我们来看一下ThreadLocalMap的创建。
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
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);
}
private Entry[] table;
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
这里又做了以下几件事
- 创建一个类型为Entry的数组,table,初始值为16。Entry是内部类,继承弱引用,
- 根据将ThreadLocal这个对象进行hash处理,获得下标i。
- 以ThreadLocal为对象,传入的值为value,本例中也就是false,创建Entry对象,放入下标为i的table数组中,设置size = 1
- 设置阈值,这个阈值为了以后的扩容用。
- 如果map不为null,也就是在demo的线程1中调用第二个set,则调用map.set(this, value);
/**
* Set the value associated with key.
*
* @param key the thread local object
* @param value the value to be set
*/
private void set(ThreadLocal<?> key, Object value) {
// We don't use a fast path as with get() because it is at
// least as common to use set() to create new entries as
// it is to replace existing ones, in which case, a fast
// path would fail more often than not.
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();
}
以上步骤可以总结为
- 遍历tab数组,如果之前的key有值,那么直接替换为新的value,如果key为null,则
- 遍历tab数组完没有找到,则新建一个Entry放入中,下标为i,同时size+1,然后判断如果没有废弃的并且当前的个数打印阈值,则进行扩容,新容量为旧容量的2倍 那么现在set的方法原理就讲完了。
2. ThreadLocal的get方法
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
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();
}
- 获取当前线程的ThreadLocalMap,那么根据set的分析,现在肯定是不为null的
- 查看map.getEntry(this)
/**
* Get the entry associated with key. This method
* itself handles only the fast path: a direct hit of existing
* key. It otherwise relays to getEntryAfterMiss. This is
* designed to maximize performance for direct hits, in part
* by making this method readily inlinable.
*
* @param key the thread local object
* @return the entry associated with key, or null if no such
*/
private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
根据当前的ThreadLocal计算出下标,然后根据下标查找table,
- 如果不为null,也就是demo中的线程1,并且key也符合,则返回Entry,然后返回Entry的value,也就是所需要的值。
- 如果为null,也就是demo中的线程2(因为线程2中没有set值),则调用getEntryAfterMiss方法,辞职的e为null
/**
* Version of getEntry method for use when key is not found in
* its direct hash slot.
*
* @param key the thread local object
* @param i the table index for key hash code
* @param e the entry at table[i]
* @return the entry associated with key, or null if no such
*/
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
return null;
}
因为传入的e为null,所以while循环不执行,直接返回null,也就是我们打印的结果。
总结
1个Thread对应一个ThreadLocalMap,ThreadLocalMap中有个类型为Entry为的table数值,采用懒加载的方式,当调用set方法时才初始化,Entry的key为ThreadLocal对象,所以Thread 和 ThreadLocal是一对多的关系。table的下标是ThreadLocal对象的哈希码与上table容量的结果,get时也是这么计算,然后在table中寻找的。