Handler消息机制(三):一个线程有几个Looper?如何保证?(1)

*/

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();

最后

都说三年是程序员的一个坎,能否晋升或者提高自己的核心竞争力,这几年就十分关键。

技术发展的这么快,从哪些方面开始学习,才能达到高级工程师水平,最后进阶到Android架构师/技术专家?我总结了这 5大块;

我搜集整理过这几年阿里,以及腾讯,字节跳动,华为,小米等公司的面试题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 PDF(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

Java语言与原理;
大厂,小厂。Android面试先看你熟不熟悉Java语言

高级UI与自定义view;
自定义view,Android开发的基本功。

性能调优;
数据结构算法,设计模式。都是这里面的关键基础和重点需要熟练的。

NDK开发;
未来的方向,高薪必会。

前沿技术;
组件化,热升级,热修复,框架设计

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!
题,把面试的要求和技术点梳理成一份大而全的“ Android架构师”面试 PDF(实际上比预期多花了不少精力),包含知识脉络 + 分支细节。

[外链图片转存中…(img-V9InqUyb-1714752975163)]

Java语言与原理;
大厂,小厂。Android面试先看你熟不熟悉Java语言

[外链图片转存中…(img-3deAxWE8-1714752975164)]

高级UI与自定义view;
自定义view,Android开发的基本功。

[外链图片转存中…(img-yqjyW8jI-1714752975165)]

性能调优;
数据结构算法,设计模式。都是这里面的关键基础和重点需要熟练的。

[外链图片转存中…(img-j2HxgjGM-1714752975166)]

NDK开发;
未来的方向,高薪必会。

[外链图片转存中…(img-q9Ur2iQO-1714752975168)]

前沿技术;
组件化,热升级,热修复,框架设计

[外链图片转存中…(img-0X0V1be3-1714752975169)]

网上学习 Android的资料一大堆,但如果学到的知识不成体系,遇到问题时只是浅尝辄止,不再深入研究,那么很难做到真正的技术提升。希望这份系统化的技术体系对大家有一个方向参考。

我在搭建这些技术框架的时候,还整理了系统的高级进阶教程,会比自己碎片化学习效果强太多

当然,想要深入学习并掌握这些能力,并不简单。关于如何学习,做程序员这一行什么工作强度大家都懂,但是不管工作多忙,每周也要雷打不动的抽出 2 小时用来学习。

不出半年,你就能看出变化!
《Android学习笔记总结+移动架构视频+大厂面试真题+项目实战源码》点击传送门,即可获取!

  • 16
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值