ThreadLocal的源码分析

概序

如果你还不知道ThreadLocal是什么东西,请看上一篇 ThreadLocal的快速入门
这篇将会讲到ThreadLocal的一些api,然后从源码分析ThreadLocal的工作原理,关于ThreadLocal的使用场景比较少,比如Loop、ActivityThread、ActivityManagerService

ThreadLocal的4个常用api

  1. initialValue() :返回此线程局部变量的当前线程的“初始值”。
  2. get():返回此线程局部变量的当前线程副本中的值。
  3. set():返回此线程局部变量的当前线程副本中的值。
  4. remove():移除此线程局部变量当前线程的值。

使用ThreadLocal最重要是了解set()方法和get()方法

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

从源码中可以看出,set方法首先是获取当前线程,然后将当前线程传给getMap()方法,返回一个ThreadLocalMap对象


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

getMap()方法调用线程的threadLocals,通过查看Thread类发现默认为null,没有做实现,所以会调用createMap()方法,

void createMap(Thread t, T firstValue) {
        t.threadLocals = new ThreadLocalMap(this, firstValue);
    }

createMap方法中通过当前线程和要设置的值value new了一个ThreadLocalMap,并且将new的实例赋值给当前线程的threadLocals

ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
            table = new Entry[INITIAL_CAPACITY];
            int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
            table[i] = new Entry(firstKey, firstValue);
            size = 1;
            setThreshold(INITIAL_CAPACITY);
        }

从源码可以看出通过key和value构造一个Entry对象,并且放到table数组里面。这里的table比较重要,后面的get方法也会用到。这里我们已经了解了set方法的工作原理

get():

public T get() {
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null) {
            ThreadLocalMap.Entry e = map.getEntry(this);
            if (e != null) {
                @SuppressWarnings("unchecked")
                T result = (T)e.value;
                return result;
            }
        }
        return setInitialValue();
    }

get()方法首先是获取当前的线程,然后调用getMap(t);通过当前线程获取值

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

getMap()方法通过当前线程获取threadLocals,由于之前调用了set方法,所以此时的threadLocals不会为null。在threadLocals不为null的情况下,调用getEntry()方法获取值,如果值不为null的情况下,返回;如果上面条件不满足的话,调用setInitialValue()方法,

private T setInitialValue() {
        T value = initialValue();
        Thread t = Thread.currentThread();
        ThreadLocalMap map = getMap(t);
        if (map != null)
            map.set(this, value);
        else
            createMap(t, value);
        return value;
    }

他会调用initialValue()进行初始化一个value,然后和set方法类似的了。到这里get方法的实现原理我们也了解了。

最后再看看另外两个api

  • initialValue():线程第一次使用 get() 方法访问变量时将调用此方法,但如果线程之前调用了 set(T) 方法,则不会对该线程再调用 initialValue 方法。通常,此方法对每个线程最多调用一次,但如果在调用 get() 后又调用了 remove(),则可能再次调用此方法。 这点可以从get方法的源码中可以看出
  • remove(): 如果此线程局部变量随后被当前线程读取,且这期间当前线程没有设置其值,则将调用其 initialValue() 方法重新初始化其值。这将导致在当前线程多次调用 initialValue 方法,看如下代码。
public void remove() {
         ThreadLocalMap m = getMap(Thread.currentThread());
         if (m != null)
             m.remove(this);
     }
ThreadLocalMap getMap(Thread t) {
        return t.threadLocals;
    }
 /**
         * Remove the entry for key.
         */
        private void remove(ThreadLocal<?> key) {
            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)]) {
                if (e.get() == key) {
                    e.clear();
                    expungeStaleEntry(i);
                    return;
                }
            }
        }

OK,到这里,ThreadLocal的分析将全部完成了。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值