Handler---4部曲---1. 总体流程
Handler---4部曲---2. ThreadLocal 存储主流程
Handler---4部曲---3. MessageQueue队列
Handler---4部曲---4.细节补充
前言
看Handler源码, 每次一说到ThreadLocal的保存, 几乎都说可以理解成key就是当前线程, value就是对应的值. 实际上真的是这样吗?
主要了解整个存储的流程,细节方面不做讲解.
先上图
ThreadLocal 类之间的关系.png ThreadLocal 存储流程图.png一. 先记住
每个ThreadLocal对象中,threadLocalHashCode 的值是固定的
//ThreadLocal类中
final int threadLocalHashCode = nextHashCode();
注意:
- table数组的角标 i 和 ThreadLocal.threadLocalHashCode 有关.
- key当前ThreadLocal对象
- value 就是存入的值
二. ThreadLocal.set() 方法
public void set(T value) {
Thread t = Thread.currentThread();
//从当前线程中, 获取成员变量 threadLocals
ThreadLocalMap map = getMap(t);
if (map != null)
//保存数据
map.set(this, value);
else
// 首次进入,创建一个ThreadLocalMap,并赋值给当前线程
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
步骤:
- 从当前线程取出成员变量:ThreadLocalMap threadLocals;
- threadLocals为空, 创建ThreadLocalMap, 并赋值.
- threadLocals不为空,直接设置值
- 每个线程保存的本质, 就是在当前线程中的ThreadLocalMap对象中进行存储.
三. 下面看看 new ThreadLocalMap() 和 ThreadLocalMap.set() 都做了些什么?
3.1 ThreadLocalMap构造方法
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
//初始化table数组, 数组里面是没有值的
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
//保存对应的值
table[i] = new Entry(firstKey, firstValue);
}
步骤:
- 初始化table数组---->没有赋值
- tab[i] 保存值
3.2 ThreadLocalMap中的set方法
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
//tab的位置
int i = key.threadLocalHashCode & (len-1);
//Entry e = tab[i] --->当前 ThreadLocal 对应的值
// 如果tab[i] 有值, 证明之前保存过
for (Entry e = tab[i];
e != null;
//这里更新了i, i = i+1
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//同一个ThreadLocal, 直接覆盖
if (k == key) {
e.value = value;
return;
}
}
//如果 tab[i] == null, 直接保存
tab[i] = new Entry(key, value);
int sz = ++size;
//判断table数组是否需要扩容
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
步骤:
- 查看tab[i]是否有值?
- 如果有值,并且key也相同, 覆盖原来的值
- 如果没有值,直接赋值.
- 判断table数组 是否扩容.
结论:
1. 一个线程Thread, 只有一个ThreadLocalMap成员变量, 可以有多个ThreadLocal对象
2. 同一个线程,多个ThreadLocal时, 存储的值在同一个Thread.ThreadLocalMap里面的 table数组. 类型Entry[]
3. 不同线程, 同一个ThreadLocal时, 存储在不同的Thread对象中. 1个Thread 对应 1个 ThreadLocalMap
网友评论