Android如何保证一个线程最多只能有一个Looper

我们来看一下 ThreadLocal 的 get 方法:

由上图我们看到,get 方法是通过当前的线程thread,取到一个 ThreadLocalMap 对象(这个就把他当做 map 集合对待即可)。

如果得到的map 是空,则进行这个 map 的初始化:

setInitialValue()。

而初始化的方式也很简单,就是创建一个以 ThreadLocal 自身为 key,存入的对象为 value 的 ThreadLocalMap 对象,然后把这个 ThreadLocalMap 存到线程 thread 里面(方便与线程进行绑定)。

如果得到的 map 不为空

则从 ThreadLocalMap 中获取对应的存储对象,如果没有向 ThreadLocal 里调用 set() 方法,这个时候调用 get() 方法返回的值就是 null。

回想一下 Looper 的 prepare() 方法

就很符合这个逻辑。

prepare() 方法会判断 threadLocal 的 get() 方法,如果返回值不为 null,说明调用过了 threadLocal 的set() 方法了。

此时 set 方法其实也无需再看了,无非就是将需要存储的对象当做value,当前的 threadLocal 的引用当做key,存储进 threadLocalMap 里面,然后将其指向线程thread。

所以总述一下,Looper 是通过利用 ThreadLocal 的数据隔离性将 Looper 对象存储在对应的线程中,保证每一个线程只能创建一个 Looper 对象。

参考地址:https://baijiahao.baidu.com/s?id=1672811852535456858&wfr=spider&for=pc

3. ThreadLocal


ThreadLocal位于java.lang包中,以下是JDK文档中对该类的描述

Implements a thread-local storage, that is, a variable for which each thread has its own value. All threads share the same ThreadLocal object, but each sees a different value when accessing it, and changes made by one thread do not affect the other threads. The implementation supports null values.

大致意思是,ThreadLocal实现了线程本地存储。所有线程共享同一个ThreadLocal对象,但不同线程仅能访问与其线程相关联的值,一个线程修改ThreadLocal对象对其他线程没有影响。

ThreadLocal为编写多线程并发程序提供了一个新的思路。如下图所示,我们可以将ThreadLocal理解为一块存储区,将这一大块存储区分割为多块小的存储区,每一个线程拥有一块属于自己的存储区,那么对自己的存储区操作就不会影响其他线程。对于ThreadLocal,则每一小块存储区中就保存了与特定线程关联的Looper。

这里写图片描述

3. ThreadLocal的内部实现原理


3.1 Thread、ThreadLocal和Values的关系


Thread的成员变量localValues代表了线程特定变量,类型为ThreadLocal.Values。由于线程特定变量可能会有多个,并且类型不确定,所以ThreadLocal.Values有一个table成员变量,类型为Object数组。这个localValues可以理解为二维存储区中与特定线程相关的一列。

ThreadLocal类则相当于一个代理,真正操作线程特定存储区table的是其内部类Values。

这里写图片描述

这里写图片描述

3.2 set方法


public void set(T value) {

Thread currentThread = Thread.currentThread();

Values values = values(currentThread);

if (values == null) {

values = initializeValues(currentThread);

}

values.put(this, value);

}

Values values(Thread current) {

return current.localValues;

}

既然与特定线程相关,所以先获取当前线程,然后获取当前线程特定存储,即Thread中的localValues,若localValues为空,则创建一个,最后将value存入values中。

void put(ThreadLocal<?> key, Object value) {

cleanUp();

// Keep track of first tombstone. That’s where we want to go back

// and add an entry if necessary.

int firstTombstone = -1;

for (int index = key.hash & mask;; index = next(index)) {

Object k = table[index];

if (k == key.reference) {

// Replace existing entry.

table[index + 1] = value;

return;

}

if (k == null) {

if (firstTombstone == -1) {

// Fill in null slot.

table[index] = key.reference;

table[index + 1] = value;

size++;

return;

}

// Go back and replace first tombstone.

table[firstTombstone] = key.reference;

table[firstTombstone + 1] = value;

tombstones–;

size++;

return;

}

// Remember first tombstone.

if (firstTombstone == -1 && k == TOMBSTONE) {

firstTombstone = index;

}

}

}

从put方法中,ThreadLocal的reference和值都会存进table,索引分别为index和index+1。

对于Looper这个例子,

table[index] = sThreadLocal.reference;(指向自己的一个弱引用)

table[index + 1] = 与当前线程关联的Looper。

3.3 get方法


public T get() {

// Optimized for the fast path.

Thread currentThread = Thread.currentThread();

Values values = values(currentThread);

if (values != null) {

Object[] table = values.table;

int index = hash & values.mask;

if (this.reference == table[index]) {

return (T) table[index + 1];

}

} else {

values = initializeValues(currentThread);

}

return (T) values.getAfterMiss(this);

}

首先取出与线程相关的Values,然后在table中寻找ThreadLocal的reference对象在table中的位置,然后返回下一个位置所存储的对象,即ThreadLocal的值,在Looper这个例子中就是与当前线程关联的Looper对象。

从set和get方法可以看出,其所操作的都是当前线程的localValues中的table数组,所以不同线程调用同一个ThreadLocal对象的set和get方法互不影响,这就是ThreadLocal为解决多线程程序的并发问题提供了一种新的思路。

4. ThreadLocal背后的设计思想Thread-Specific Storage模式


Thread-Specific Storage让多个线程能够使用相同的”逻辑全局“访问点来获取线程本地的对象,避免了每次访问对象的锁定开销。

4.1 Thread-Specific Storage模式的起源

尾声

最后,我再重复一次,如果你想成为一个优秀的 Android 开发人员,请集中精力,对基础和重要的事情做深度研究。

对于很多初中级Android工程师而言,想要提升技能,往往是自己摸索成长,不成体系的学习效果低效漫长且无助。 整理的这些架构技术希望对Android开发的朋友们有所参考以及少走弯路,本文的重点是你有没有收获与成长,其余的都不重要,希望读者们能谨记这一点。

最后想要拿高薪实现技术提升薪水得到质的飞跃。最快捷的方式,就是有人可以带着你一起分析,这样学习起来最为高效,所以为了大家能够顺利进阶中高级、架构师,我特地为大家准备了一套高手学习的源码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

进阶学习视频

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

码和框架视频等精品Android架构师教程,保证你学了以后保证薪资上升一个台阶。

当你有了学习线路,学习哪些内容,也知道以后的路怎么走了,理论看多了总要实践的。

进阶学习视频

[外链图片转存中…(img-jzwFG2VU-1719743595990)]

附上:我们之前因为秋招收集的二十套一二线互联网公司Android面试真题 (含BAT、小米、华为、美团、滴滴)和我自己整理Android复习笔记(包含Android基础知识点、Android扩展知识点、Android源码解析、设计模式汇总、Gradle知识点、常见算法题汇总。)

[外链图片转存中…(img-6ifbTKXc-1719743595991)]

  • 3
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Android 中的 Looper 机制是一个消息循环系统,用于管理应用程序中的消息队列。在 Python 中,我们可以使用 `queue` 模块来实现类似的机制。以下是一个使用 `queue` 模块实现的死循环示例: ```python import queue def my_handler(): while True: try: message = my_queue.get(block=True, timeout=1) except queue.Empty: continue # 处理消息 print(f"Received message: {message}") # 将消息放回队列 my_queue.put(message) # 创建消息队列 my_queue = queue.Queue() # 启动消息处理循环 my_handler() ``` 在这个示例中,我们定义了一个 `my_handler()` 函数,它包含一个无限循环,它会从一个队列中获取消息,并对消息进行处理。如果队列为空,循环将等待 1 秒钟,然后重试。在处理完消息后,循环将消息放回队列中,以便其他线程可以处理它。 要使用这个循环,我们需要创建一个队列,并将消息放入队列中。例如: ```python # 将两条消息放入队列中 my_queue.put("Hello") my_queue.put("World") # 等待一段时间 time.sleep(5) # 取出队列中的消息 while not my_queue.empty(): message = my_queue.get() print(f"Got message: {message}") ``` 在这个示例中,我们创建了一个队列,并将两条消息放入队列中。然后,我们等待 5 秒钟,并从队列中取出所有的消息并打印它们。在此过程中,`my_handler()` 函数将一直运行,并处理队列中的消息,直到程序结束。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值