由浅入深全面剖析ThreadLocal

前言

这一阵子一直在看Picasso,在看的过程中发现了很多很有意思的东西,有的是以前见过甚至用过但是没有深入关注的,有些是以前根本没有见过的——比如今天要讲的ThreadLocal。(android 6.0)

正文

1,ThreadLocal是什么?

先看一下Android官网的文档:

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对象,但是每个线程只能访问自己所存储的变量,并且线程之间做的改动互不影响。此实现支持null变量的存储。(非逐词翻译)

通过这个描述,我们可以知道一些信息:

  • ThreadLocal是一个线程的内部存储类,通过它我们可以在指定的线程中存储信息。
  • 每个线程之间的信息是独立且封闭的。

但是同时也会有一些疑问:

  • 所谓的自有变量是什么?
  • 通过什么形式存储?
  • 怎么对存储的信息进行操作?
  • 怎么实现的线程间的信息封闭且独立?
  • 等等

接下来我们就慢慢的在探索这个类的过程中,来从头到尾的弄清楚这些个疑问。

2,ThreadLocal的用途

ThreadLocal的作用是实现每个线程的自有变量的存储,这个“自有变量”具体是什么当然要根据不同的需求来定,但是在Android的源码中,这个自有变量常常是Looper。

这个Looper是什么呢?这里就不得不提到Android的消息机制了。Android的消息机制是指Handler的运行机制以及Handler所附带的MessageQueue和Looper的工作过程。简单来讲,就是Handler发送Message到MessageQueue中,而Looper不断的从MessageQueue中循环摘取Message,并进行进一步的解析处理。

但是Looper只是个简单的类而已,它虽然提供了循环处理方面的成员函数loop(),却不能自己凭空地运行起来,而只能寄身于某个真实的线程。那么Looper是怎么和线程建立联系的呢?这个时候ThreadLocal就参与其中了。

我们来看一下Looper的部分源码:

private static void prepare(boolean quitAllowed) {
        if (sThreadLocal.get() != null) {
            throw new RuntimeException("Only one Looper may be created per thread");
        }
        //调用ThreadLocal的set()方法将Looper存进去
        sThreadLocal.set(new Looper(quitAllowed));
}
 public static void loop() {
        //调用myLooper()方法得到Looper,我们接着看一下myLooper()方法
        final Looper me = myLooper();
        if (me == null) {
            throw new RuntimeException("No Looper; Looper.prepare() wasn't called on this thread.");
        }
        …… ……
}
public static @Nullable Looper myLooper() {
        //调用ThreadLocal的get()方法得到刚才存入的Looper对象
        return sThreadLocal.get();
}

关于ThreadLocal的set()和get()方法是怎么对Looper进行操作并将其存入Thread中的下文会有详述。在这里我们可以清楚的看到ThreadLocal的踪迹并对它的用途有一个比较清晰的认识:当某个变量是与线程相关的并且不同线程具有不同值的时候,我们就可以考虑使用ThreadLocal这个类来简化我们的工作(比如Looper)。

更多ThreadLocal的具体用途大家可以看下任教主的这篇博文:Android的消息机制之ThreadLocal的工作原理

3,ThreadLocal用法

它的用法其实挺简单的,暴露出来的方法一共只有三个:

  • get():返回调用线程中变量的当前值
  • set(T value):设置调用线程中变量的值
  • remove():移除调用线程的当前变量

用的话也是这三个方法,就跟上面Looper里面的用法差不多。例子的代码如下:

mIntegerThreadLocal = new ThreadLocal<>();

mIntegerThreadLocal.set(0);
//在主线程里设置mIntegerThreadLocal的值为0,输出0
Log.d(TAG, "[Thread#main]mIntegerThreadLocal=" + mIntegerThreadLocal.get());

new Thread("Thread#1") {
    @Override
    public void run() {
        //设置mIntegerThreadLocal的值为1,输出1
        mIntegerThreadLocal.set(1);
        Log.d(TAG, "[Thread#1]mIntegerThreadLocal=" + mIntegerThreadLocal.get());
    }
}.start();

new Thread("Thread#2") {
    @Override
    public void run() {
        //不设置任何值,输出null
        Log.d(TAG, "[Thread#2]mIntegerThreadLocal=" + mIntegerThreadLocal.get());
    }
}.start();

new Thread("Thread#3") {
    @Override
    public void run() {
        //设置为3,然后remove,输出null
        mIntegerThreadLocal.set(3);
        mIntegerThreadLocal.remove();
        Log.d(TAG, "[Thread#3]mIntegerThreadLocal=" + mIntegerThreadLocal.get());
    }
}.start();

输出结果如下:

05-08 16:08:24.771 14423-14423/com.lypeer.apifinder D/MainActivity: [Thread#main]mIntegerThreadLocal=0
05-08 16:08:24.774 14423-14447/com.lypeer.apifinder D/MainActivity: [Thread#2]mIntegerThreadLocal=null
05-08 16:08:24.780 14423-14446/com.lypeer.apifinder D/MainActivity: [Thread#1]mIntegerThreadLocal=1
05-08 16:08:24.781 14423-14448/com.lypeer.apifinder D/MainActivity: [Thread#3]mIntegerThreadLocal=null

事实证明,ThreadLocal确实做到了官方文档里说到的功能:存储Thread的信息,并且每个线程之间信息独立,即它们只能访问自己对应的信息,并且修改自己对应的信息之后对其他线程没有影响。

4,源码解析

4.1,构造方法

先看一下它的构造方法:

 public ThreadLocal() {}

可以的,啥都没干,空构造,这条路子走不通。

4.2,暴露方法

4.2.1,public void set(T value)

接下来从暴露出来的方法入手,先看set()方法:


                
  • 1
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
ThreadLocal是Java中的一个线程本地变量,它提供了一种简单的解决多线程并发访问共享变量的方案。每个ThreadLocal对象维护了一个独立的变量副本,每个线程都可以访问自己的变量副本,从而避免了多线程之对同一变量的竞争。 ThreadLocal的原理可以简单概括为以下几个步骤: 1. 每个Thread对象内部都有一个ThreadLocalMap对象,ThreadLocalMap是ThreadLocal的核心数据结构,用于存储每个ThreadLocal对象对应的变量副本。 2. 在ThreadLocal对象中调用set()方法时,ThreadLocal首先获取当前线程的ThreadLocalMap对象,然后将当前ThreadLocal对象作为key,将要存储的变量副本作为value,存储到ThreadLocalMap中。 3. 在ThreadLocal对象中调用get()方法时,ThreadLocal首先获取当前线程的ThreadLocalMap对象,然后以当前ThreadLocal对象作为key获取对应的变量副本,从而实现了多线程之的变量隔离。 4. 在ThreadLocal对象中调用remove()方法时,ThreadLocal首先获取当前线程的ThreadLocalMap对象,然后将当前ThreadLocal对象从ThreadLocalMap中删除。 需要注意的是,由于ThreadLocalMap是存储在线程中的,因此需要注意内存泄漏的问题。如果在ThreadLocal对象使用结束后没有调用remove()方法,会导致ThreadLocal对象一直存在于ThreadLocalMap中,从而引起内存泄漏。因此,在使用ThreadLocal对象时,需要注意及时调用remove()方法,以避免内存泄漏问题。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值