*/
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();
}
在这个方法中,有一个变量叫做table,ThreadLocal的值就存在这个table中。Entry是Map中用来保存一个键值对的,而Map实际上就是多个Entry的集合,Entry<key,value>和Map<key,value>一样的理解方式。
在Android M(Android 6.0)之前,数据结构是通过Values实现的,Values中也有一个table的成员变量,table是一个Object数组,也是以类似map的方式来存储的。偶数单元存储的是key,key的下一个单元存储的是对应的value,所以每存储一个元素,需要两个单元,所以容量一定是2的倍数。
/**
-
The table, resized as necessary.
-
table.length MUST always be a power of two.
*/
private Entry[] table;
此哈希映射中的条目使用它的主ref字段作为键(它总是线程本地对象)。注意空键(即entry.get() == null)表示不再引用密钥,因此条目可以从表中删除。
Entry 继承了 WeakReference,那么通过 Entry 对象的 get 方法就可以获取到一个弱引用的 ThreadLocal 对象,Entry 保存了 ThreadLocal(key) 和 对应的值(value),其中 ThreadLoacl 是通过弱引用的形式,避免了线程池线程复用带来的内存泄露。
/**
-
The entries in this hash map extend WeakReference, using
-
its main ref field as the key (which is always a
-
ThreadLocal object). Note that null keys (i.e. entry.get()
-
== null) mean that the key is no longer referenced, so the
-
entry can be expunged from table. Such entries are referred to
-
as “stale entries” in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
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();
}
get()方法中,首先获取了当前的线程,然后又根据当前的线程取出map。
/**
-
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;
}
这个getMap就是拿到了当前线程的ThreadLocalMap,继续回到get()方法里。
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings(“unchecked”)
T result = (T)e.value;
return result;
}
}
如果获取的ThreadLocalMap这个map不为空,则以ThreadLocal的引用作为Key,在map中获取对应的Entry对象;如果获取的Entry对象也不为空的话,把它的value值返回出来。
在该方法的最后一句,也就是说当map为空的时候,则直接返回这个方法的结果。
return setInitialValue();
这个setInitialValue()方法是做什么的呢?
/**
-
Variant of set() to establish initialValue. Used instead
-
of set() in case user has overridden the set() method.
- @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
首先,它先用initialValue()方法把value值获取到,然后拿到当前的线程Thread,用当前线程获取一遍ThreadLocalMap,如果这个map不是空的话,就以ThreadLocal的引用为Key,以获取到的value值为Value,往这map里设值;如果map是空的,就拿引用和value作为第一个Key和第一个Value创建一个新的map。
/**
-
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);
}
通过看ThreadLocal的get()和set()方法,发现他们所操作的对象都是当前(各自)线程的LocalValues对象的table数组,他们对ThreadLocal所做的读写操作都仅限于各自的线程范围内,怪不得ThreadLocal在各自线程中可以互不干扰的对数据进行读写操作。
如何保证一个线程中只有一个Looper?
通过上面对ThreadLocal的get中看,首先获取当前线程的ThreadLocalMap,如果map为空,返回的就是setInitialValue()。
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为空,就创建一个ThreadLocalMap,这个map里先存上第一个键值对(当前threadLocal为键,null为值);如果map不为空,就根据键找键值对,找到了,就返回键值对中的值,如果找不到,还是会调用setInitialValue(),存上键值对(当前threadLocal为键,null为值)。
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!
果找不到,还是会调用setInitialValue(),存上键值对(当前threadLocal为键,null为值)。
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
最后
自我介绍一下,小编13年上海交大毕业,曾经在小公司待过,也去过华为、OPPO等大厂,18年进入阿里一直到现在。
深知大多数初中级Android工程师,想要提升技能,往往是自己摸索成长,自己不成体系的自学效果低效漫长且无助。
因此我收集整理了一份《2024年Android移动开发全套学习资料》,初衷也很简单,就是希望能够帮助到想自学提升又不知道该从何学起的朋友,同时减轻大家的负担。
[外链图片转存中…(img-qanzvijP-1715622662941)]
[外链图片转存中…(img-hOXCPvMh-1715622662942)]
[外链图片转存中…(img-GFecOvSp-1715622662943)]
[外链图片转存中…(img-xTQAHvQu-1715622662944)]
既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,基本涵盖了95%以上Android开发知识点!不论你是刚入门Android开发的新手,还是希望在技术上不断提升的资深开发者,这些资料都将为你打开新的学习之门
如果你觉得这些内容对你有帮助,需要这份全套学习资料的朋友可以戳我获取!!
由于文件比较大,这里只是将部分目录截图出来,每个节点里面都包含大厂面经、学习笔记、源码讲义、实战项目、讲解视频,并且会持续更新!