前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >android Handler机制之ThreadLocal详解

android Handler机制之ThreadLocal详解

作者头像
xiangzhihong
发布2018-02-05 17:01:56
1.2K0
发布2018-02-05 17:01:56
举报
文章被收录于专栏:向治洪向治洪向治洪

概述

我们在谈Handler机制的时候,其实也就是谈Handler、Message、Looper、MessageQueue之间的关系,对于其工作原理我们不做详解(Handler机制详解)。

  • Message:Handler发送、接收和处理的消息对象
  • Looper:每个线程只能拥有一个Looper.它的looper()方法负责循环读取MessageQueue中的消息并将读取到的消息交给发送该消息的handler进行处理。
  • MessageQueue:消息队列,它采用先进先出的方式来管理Message。程序在创建Looper对象时,会在它的构造器中创建MessageQueue。

Handler类简析

Handler类的主要作用有两个:在新启动的线程中发送消息;在主线程中获取和处理消息。

而要完整的理解Handler机制,对于Looper的底层存储和轮询机制是必须了解的,看过了其实就很简单,今天就专门讲这个。

ThreadLocal详解

为了方便大家理解,我们直接看源码:

public class ThreadLocal<T> {
 .....
}

这里可以看出threadlocal是一个范型类,这标志着threadlocal可以存储所有数据,作为存储数据来说,我们首先想到的是会对外提供set(),get(),remove(),等方法。

set()方法:

 /**
     * Sets the value of this variable for the current thread. If set to
     * {@code null}, the value will be set to null and the underlying entry will
     * still be present.
     *
     * @param value the new value of the variable for the caller thread.
     */
    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方法:

/**
     * Gets Values instance for this thread and variable type.
     */
    Values values(Thread current) {
        return current.localValues;
    }

该方法是返回当前线程的一个存储实类,那ThreadLocal又是什么呢?上面说过 ThreadLocal是一个线程内部的数据存储类,通过它可以在指定的线程中存储数据,数据存储以后,只有在指定线程中可以获取到存储的数据。

我们来看几个ThreadLocal方法,先回到set方法,得到values的实类以后会来一个判断,为null调用initializeValues(currentThread)

 Values initializeValues(Thread current) {
        return current.localValues = new Values();
    }

接下来调用value的put方法,我们想到的应该是往里面插值,也就是我们说的put()。

 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;
                }
            }
        }

从源码可以看出,把values的值传入到一个table数组的key.reference的下一个下标中,至此,我们了解了Threadlocal的存值过程,首先会获取当前线程,根据当前线程获取Values存储类,该存储类在该线程是单例的,在调用values存储类中的put方法,最终将存储的内容存储到Values内部类的table数组下标为key.reference中 。

接下来我们来看一下取值的方法:

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

ThreadLocal的get方法的逻辑也比较清晰,它同样是取出当前线程的localValues对象,如果这个对象为null那么就返回初始值,初始值由ThreadLocal的initialValue方法来描述。

  protected T initialValue() {
        return null;
    }

如果localValues对象不为null,那就取出它的table数组并找出ThreadLocal的reference对象在table数组中的位置,然后table数组中的下一个位置所存储的数据就是ThreadLocal的值。

接着我们再来看一下remove的方法实现:

 void remove(ThreadLocal<?> key) {
            cleanUp();

            for (int index = key.hash & mask;; index = next(index)) {
                Object reference = table[index];

                if (reference == key.reference) {
                    // Success!
                    table[index] = TOMBSTONE;
                    table[index + 1] = null;
                    tombstones++;
                    size--;
                    return;
                }

                if (reference == null) {
                    // No entry found.
                    return;
                }
            }
        }

到此我们就完全明白了ThreadLocal的存取原理了:通过ThreadLocal的set和get方法可以看出,它们所操作的对象都是当前线程的localValues对象的table数组,因此在不同线程中访问同一个ThreadLocal的set和get方法,它们对ThreadLocal所做的读写操作仅限于各自线程的内部,这就是为什么ThreadLocal可以在多个线程中互不干扰地存储和修改数据。

本文参与 腾讯云自媒体分享计划,分享自作者个人站点/博客。
原始发表:2016-12-26 ,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 概述
    • Handler类简析
    • ThreadLocal详解
    相关产品与服务
    数据保险箱
    数据保险箱(Cloud Data Coffer Service,CDCS)为您提供更高安全系数的企业核心数据存储服务。您可以通过自定义过期天数的方法删除数据,避免误删带来的损害,还可以将数据跨地域存储,防止一些不可抗因素导致的数据丢失。数据保险箱支持通过控制台、API 等多样化方式快速简单接入,实现海量数据的存储管理。您可以使用数据保险箱对文件数据进行上传、下载,最终实现数据的安全存储和提取。
    领券
    问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档