深入分析Java的ThreadLocal

上回书说,Android可以用Looper+Handler来实现线程通信的关键是在于Looper 回顾:深入了解Android的Looper Looper 在当前线程里维护了一个MessageQueue,并不断从中取Message出来给Handler去处理。 我们留了个问题,Looper是个全局类,它通过ThreadLocal来保证每个线程只能获取到自己的Looper,那么它是怎么做到的呢?

Java之ThreadLocal

ThreadLocal是java.lang包下的类,它是一个用来创建线程局部变量的类。先来看一段代码,

final ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("test threadlocal");
new Thread(){
    @Override
    public void run() {
        Log.d(TAG, "value: " + threadLocal.get());
    }
}.start();

这段代码其实打印不出threadlocal中的内容,而是直接输出null

:D/ThreadLocal value: null

因为我们是在另外一个线程中set的数据,因此在新线程中是读不到这个值的。 Looper同理,sThreadLocal在初始化时以Looper作为泛型,每当我们调用prepare()时,都会实例化一个Looper并放入sThreadLocal中,因为这个Looper是在当前线程创建的,所以当我们在这个线程中调用myLooper()时,也只能从sThreadLocal获取到当前线程的Looper。

ThreadLocal的实现

一般面试时能了解到这个层面基本算中高级工程师了,当然如果要到高级或者资深,就不得不了解更深一层的原理实现。 ThreadLocal的实现Java和Android略有不同,但原理相差无二。我们可以从 set()方法入手。

public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;
}

ThreadLocalMap 是ThreadLocal的内部类。这段代码可以看出当进行set()操作时,实际是获取当前线程的ThreadLocalMap对象并放入值。所以只能在本线程中访问,其他线程无法访问。

ThreadLocal进阶

到这里基本上对于ThreadLocal的知识点就结束了,你已经是高级程序员了。接下来的进阶部分可以让你成为资深开发。 作为一个习惯性找bug的优秀程序员,会想是否可以访问其他线程的ThreadLocal里的数据呢?答案当然是可以的。

InheritableThreadLocal

如果在ThreadLocal实例化的时候使用的是它的子类 InheritableThreadLocal,那么输出结果就会变成

final ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
threadLocal.set("test threadlocal");
new Thread(){
    @Override
    public void run() {
        Log.d(TAG, "value: " + threadLocal.get());
    }
}.start();

:D/ThreadLocal value: test threadlocal

原因是,线程创建过程中,会把父线程的inheritableThreadLocals对象复制一份放到子线程的inheritableThreadLocals,当我们用 InheritableThreadLocal的实例的get和set方法时,其实读取的是复制过后的值,所以它保留了父线程的数据。有兴趣的可以看下Thread的源码,

private void init2(Thread parent) {
    ....
    if (parent.inheritableThreadLocals != null) {
        this.inheritableThreadLocals = ThreadLocal.createInheritedMap(
                parent.inheritableThreadLocals);
    }
}

总结

· Looper使用了Java的ThreadLocal类,来保证每个线程只和当前线程创建的Looper绑定 · ThreadLocal的对象实际上是保存在Thread中,因此每个线程对Looper的获取只能获取到自己的Looper 到这里我们就结束了对Android线程交互的分析啦,希望面试时对大家有帮助~

原文发布于微信公众号 - Android每日一讲(gh_f053f29083b9)

原文发表时间:2018-03-06

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏Java学习网

常见的 Java 错误及避免方法之第五集(每集10个错误后续持续发布)

当输入期间意外终止文件或流时,将抛出“EOFException”。 以下是抛出EOFException异常的一个示例,来自JavaBeat应用程序:

16630
来自专栏函数式编程语言及工具

FunDA(17)- 示范:异常处理与事后处理 - Exceptions handling and Finalizers

    作为一个能安全运行的工具库,为了保证占用资源的安全性,对异常处理(exception handling)和事后处理(final clean-up)的支持...

19670
来自专栏Android 开发学习

JsBridge 源码分析

19130
来自专栏开发技术

结合ThreadLocal来看spring事务源码,感受下清泉般的洗涤!

  在我的博客spring事务源码解析中,提到了一个很关键的点:将connection绑定到当前线程来保证这个线程中的数据库操作用的是同一个connection...

15110
来自专栏码匠的流水账

聊聊spring cloud gateway的PrefixPath及StripPrefix功能

本文主要研究一下spring cloud gateway的PrefixPath及StripPrefix功能

19820
来自专栏程序猿DD

Spring框架中的设计模式(四)​

本文是Spring框架中使用的设计模式第四篇。本文将在此呈现出新的3种模式。一开始,我们会讨论2种结构模式:适配器和装饰器。在第三部分和最后一部分,我们将讨论单...

40460
来自专栏积累沉淀

Java设计模式(十九)----备忘录模式

备忘录模式 一、 概念 二、 结构 三、 分类 1.”白箱”备忘录模式的实现 2.“黑箱”备忘录模式的实现 3.“多重”检查点 4....

20090
来自专栏chenssy

【追光者系列】HikariCP源码分析之evict、时钟回拨、连接创建生命周期

evict定义在com.zaxxer.hikari.pool.PoolEntry中,evict的汉语意思是驱逐、逐出,用来标记连接池中的连接不可用。

47440
来自专栏服务端技术杂谈

InheritableThreadLocal源码阅读

在进行多线程编程时,我们经常需要线程池子线程和父线程进行ThreadLocal信息传递,实现一些业务处理。 先看一个例子 public class App { ...

28840
来自专栏码匠的流水账

聊聊Spring Data Auditable接口的变化

spring-data-commons-1.12.8.RELEASE-sources.jar!/org/springframework/data/domain/...

13420

扫码关注云+社区

领取腾讯云代金券