从应用角度看Android源码 - ThreadLocal 的原理

本文通过示例代码展示了ThreadLocal的基本使用方法,并详细解析了ThreadLocal的工作原理,包括ThreadLocalMap的作用及其实现细节。

最近被问了ThreadLocal的原理,之前只知道他是用来做数据的线程私有化,具体怎么用以及他的原理并没有研究过,所以这次去研究一下他的原理。首先还是创建一个新的AS项目,看看这个ThreadLocal是怎么用的,然后再一层一层去剖析他(示例代码)

先编写一个线程类,在线程内用ThreadLocal处理一个在主存中的变量,然后循环操作的local变量;

  • 错误示例代码片段 一

刚开始我写成了这个样 子,也出现了BUG(查看源代码 err1)

错误原因:

由于代码中的ThreadLocal 是子CustomThread的构造中初始化的,然而CustomThread的构造函数实在运行在UI线程,也就是这个ThreadLocal变量是属于UI线程的,然而在CustomThread的run方法是运行在行的线程里的,在run方法里获取UI线程的ThreadLocal 变量自然会返回一个空对象。

  • 错误示例代码二

针对一上一个错误代码做了一些改进,代码如下(查看源码 err2)

虽然没有报错,但是看了一些log输出按我的理解不应该是这个结果,理想状态下各自线程对refObj的操作应该是隔离的,互不影响。静下心来,然后仔细回顾了一下ThreadLocal的概念...

 ThreadLocal 所维护的变量是线程私有的,也就是说当ThreadLocal在一个全局的位置即各线程都可见的位置,分别在各自线程调用ThreadLocal.set(T)之后,ThreadLocal就为调用set的线程维护传入的这个变量T即set里的参数。ThreadLocal.get函数只能获取在本线程set的那个变量T。但是这个例子中ThreadLocal为各线程中维护的变量是同一个实例refObj,所以当在各自线程获取所维护的实例并调用其内部方法时,实际上就是在操作同一个对象,所以会受影响。用一段代码证明这件事(获取代码)

 

代码实例也就写完了,下面去研究一下内部原理.稍等片刻。

刚刚拜读了ThreadLocal 的源码,大概的原理如下:

Java 每个线程Thread都维护了一个ThreadLocalMap ,这个是真正线程私有的。ThreadLocal就是借助这个完成任务的。下面跟着代码走一下内部流程,

  1. ThreadLocal构造函数
        /**
         * Creates a thread local variable.
         */
        public ThreadLocal() {
        }

    这个构造啥都没做,直接跳过

  2. ThreadLocal.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);
    }

这个函数还算有点料,首先获取当前线程的Thread(话说忽然对这个Thread有了兴趣,下次研究一下Thread),然后在去获取这个Thread的ThreadLocalMap,即调用getMap

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

如果当前线程有ThreadLocalMap 就向Map添加value;如果没有就创建ThreadLocalMap并向创建后的Map添加Value;

直接看ThreadLocalMap的set方法

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

 

代码很简单,就是ThreadLocalMap的entry数组里添加一个Value,但是要注意一下这个key.threadLocalHashCode

 

    private final int threadLocalHashCode = nextHashCode();

    /**
     * The next hash code to be given out. Updated atomically. Starts at
     * zero.
     */
    private static AtomicInteger nextHashCode =
        new AtomicInteger();

    /**
     * The difference between successively generated hash codes - turns
     * implicit sequential thread-local IDs into near-optimally spread
     * multiplicative hash values for power-of-two-sized tables.
     */
    private static final int HASH_INCREMENT = 0x61c88647;

    /**
     * Returns the next hash code.
     */
    private static int nextHashCode() {
        return nextHashCode.getAndAdd(HASH_INCREMENT);
    }

 

它是由AtomicInteger原子操作类生成的值,这么做是为了保证在多线程环境下的唯一性,每一个ThreadLocal实例都会有一个唯一的threadLocalHashCode,这样的值也是最适合做为键值对中的Key了

 

 

 

 

 

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值