ThreadLocal

示意图

ThreadLocal

  • 顾名思义:线程本地的对象。一个ThreadLocal对象,在不同线程中,通过public T get() 函数返回的value是不同的。
  • 在Android的Looper.java中,使用了ThreadLocal对象存储线程自己的Looper对象,通过myLooper函数获取looper对象。
  • ThreadLocal存储的对象类型是T,ThreadLocal<T>. 一个ThreadLocal在每个线程中只能存储一个T类型的value值。ThreadLocal对象是存储在本线程持有的threadLocalMap中。
java/lang/ThreadLocal.java
/**
 * Sets the current thread's copy of this thread-local variable
 * to the specified value.  Most subclasses will have no need to
 * override this method, relying solely on the {@link #initialValue}
 * method to set the values of thread-locals.
 *
 * @param value the value to be stored in the current thread's copy of
 *        this thread-local.
 */
public void set(T value) {
    Thread t = Thread.currentThread(); // 获取当前的线程
    ThreadLocalMap map = getMap(t); // 获取当前Thread的 Map
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}
  • 每个线程都含有一个threadLocalMap。因为ThreadLocal对象是存储在map中的,ThreadLocal对象的T类型的value也是和线程对象强关联的。不同线程内的value值是不同。
java/lang/ThreadLocal.java
/**
 * Get the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param  t the current thread
 * @return the map
 */
ThreadLocalMap getMap(Thread t) {
    return t.threadLocals;  // 获取本线程对应的map
}

java/lang/Thread.java
/* ThreadLocal values pertaining to this thread. This map is maintained
 * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

map结构

  • Thread中的Map是一个类似hashMap的结构。有一个table[]数组,数组的类型是Entry类型。Entry实际是由ThreadLocal - T类型 组成的键值对组。
  • 每一个ThreadLocal对象都有一个默认的hash值,hash值的计算方式是通过一个AtomicInteger自增的,可以避免hash冲突。
  • ThreadLocalMap 是通过ThreadLocal对象的hash值来查找Entry键值对的。
/**
 * ThreadLocals rely on per-thread linear-probe hash maps attached
 * to each thread (Thread.threadLocals and
 * inheritableThreadLocals).  The ThreadLocal objects act as keys,
 * searched via threadLocalHashCode.  This is a custom hash code
 * (useful only within ThreadLocalMaps) that eliminates collisions
 * in the common case where consecutively constructed ThreadLocals
 * are used by the same threads, while remaining well-behaved in
 * less common cases.
 */
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); // hash值是通过AtomicInteger进行自增的。
}

/**
 * Get the entry associated with key.  This method
 * itself handles only the fast path: a direct hit of existing
 * key. It otherwise relays to getEntryAfterMiss.  This is
 * designed to maximize performance for direct hits, in part
 * by making this method readily inlinable.
 *
 * @param  key the thread local object
 * @return the entry associated with key, or null if no such
 */
private Entry getEntry(ThreadLocal<?> key) {
    int i = key.threadLocalHashCode & (table.length - 1); // 通过hash值获取Entry的数组下标
    Entry e = table[i];
    if (e != null && e.get() == key)
        return e; // T 类型的value值。
    else
        return getEntryAfterMiss(key, i, e);
}

initialValue 函数初始化ThreadLocal对象

  • initialValue 函数。通过在子类重写该函数可以初始化ThreadLocal变量。
  • get当首次get value时,如果没有初始化,那么会通过initialValue初始化ThreadLocal对象的map值。
/**
 * Returns the value in the current thread's copy of this
 * thread-local variable.  If the variable has no value for the
 * current thread, it is first initialized to the value returned
 * by an invocation of the {@link #initialValue} method.
 *
 * @return the current thread's value of this thread-local
 */
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(); // 本线程的map中value为空,尝试初始化
}

/**
 * Variant of set() to establish initialValue. Used instead
 * of set() in case user has overridden the set() method.
 *
 * @return the initial value
 */
private T setInitialValue() {
    T value = initialValue(); // 可以重写这个函数,给ThreadLocal默认的初值。
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
    return value;
}
  • 示例:EventBus中 通过重写initialValue,保证每个线程首次使用ThreadLocal时,能够获取一个初始化值。
    private final ThreadLocal<PostingThreadState> currentPostingThreadState = 
      new ThreadLocal<PostingThreadState>() {
        @Override
        protected PostingThreadState initialValue() {
            return new PostingThreadState();
        }
    };

内存泄露

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值