ThreadLocal源码分析

ThreadLocal的成员变量

    //ThreadLocal通过threadLocalHashCode来标识每一个ThreadLocal的唯一性
private final int threadLocalHashCode = nextHashCode();

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

/**
 * threadLocalHashCode通过CAS操作进行更新,每次hash操作的增量为0x61c88647
 */
private static final int HASH_INCREMENT = 0x61c88647;

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

SET方法

public void set(T value) {
    Thread t = Thread.currentThread();
    //将当前线程引用传给ThreadLocalMap对象
    ThreadLocalMap map = getMap(t);
    if (map != null)
        map.set(this, value);
    else
        createMap(t, value);
}

getMap

  //每个线程通过ThreadLocal.ThreadLocalMap与ThreadLocal相绑定。确保每个线程访问到的thread-local variable都是本线程的
 ThreadLocalMap getMap(Thread t) {
   return t.threadLocals;
 }

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

createMap

如果ThreadLocalMap不为空则调用ThreadLocalMap.ThreadLocalMap#set方法设值;

若为空则调用ThreadLocal#createMap方法new一个ThreadLocalMap实例并赋给Thread.threadLocals

    /**
 * Create the map associated with a ThreadLocal. Overridden in
 * InheritableThreadLocal.
 *
 * @param t the current thread
 * @param firstValue value for the initial entry of the map
 */
void createMap(Thread t, T firstValue) {
    t.threadLocals = new ThreadLocalMap(this, firstValue);
}

ThreadLocalMap

ThreadLocalMap是ThreadLocal的静态内部类

成员变量

/**
 * 代表Map的初始容量
 */
private static final int INITIAL_CAPACITY = 16;

/**
 * 是一个Entry类型的数组,用于存储数据
 */
private Entry[] table;

/**
 * size代表表中的存储数目
 */
private int size = 0;

/**
 * threshold代表需要扩容时对应size的阈值
 */
private int threshold; // Default to 0

private void setThreshold(int len) {
    threshold = len * 2 / 3;
}

Entry是ThreadLocalMap的静态内部类。存储数据

/**
 * 每个Entry对象都有一个ThreadLocal的弱引用(作为key),这是为了防止内存泄露。一旦线程结束,key变为一个不可达的对象,这个Entry就可以被GC了
 */
static class Entry extends WeakReference<ThreadLocal<?>> {
     Entry(ThreadLocal<?> k, Object v) {
         super(k);
         value = v;
     }
 }

ThreadLocalMap构造函数

//构造函数的第一个参数就是本ThreadLocal实例(this),第二个参数就是要保存的线程本地变量
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
     //构造函数首先创建一个长度为16的Entry数组,然后计算出firstKey对应的哈希值,然后存储到table中,并设置size和threshold
     table = new Entry[INITIAL_CAPACITY];
     int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
     table[i] = new Entry(firstKey, firstValue);
     size = 1;
     setThreshold(INITIAL_CAPACITY);
 }

计算hash的时候里面采用了hashCode & (size - 1)的算法,这相当于取模运算hashCode % size的一个更高效的实现(和HashMap中的思路相同)。正是因为这种算法,我们要求size必须是2的指数,因为这可以使得hash发生冲突的次数减小

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

如果hash碰撞 nextIndex重新计算hash值。ThreadLocalMap解决冲突的方法是线性探测法(不断加1),而不是HashMap的链地址法,这一点也能从Entry的结构上推断出来

private static int nextIndex(int i, int len) {
     return ((i + 1 < len) ? i + 1 : 0);
 }

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

若是经历了上面步骤没有命中hash,也没有发现无用的Entry,set方法就会创建一个新的Entry,并会进行启发式的垃圾清理,用于清理无用的Entry。主要通过cleanSomeSlots方法进行清理

private Entry getEntry(ThreadLocal<?> key) {
    int i = key.threadLocalHashCode & (table.length - 1);
    Entry e = table[i];
    if (e != null && e.get() == key)
        return e;
    else
        return getEntryAfterMiss(key, i, e);
}

/**
 * Version of getEntry method for use when key is not found in
 * its direct hash slot.
 *
 * @param  key the thread local object
 * @param  i the table index for key's hash code
 * @param  e the entry at table[i]
 * @return the entry associated with key, or null if no such
 */
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
    Entry[] tab = table;
    int len = tab.length;

    while (e != null) {
        ThreadLocal<?> k = e.get();
        if (k == key)
            return e;
        if (k == null)
            expungeStaleEntry(i);
        else
            i = nextIndex(i, len);
        e = tab[i];
    }
    return null;
}

rehash操作会执行一次全表的扫描清理工作,并在size大于等于threshold的四分之三时进行resize,即扩容一倍。因此ThreadLocalMap的加载因子一样为0.75。

private void resize() {
     Entry[] oldTab = table;
     int oldLen = oldTab.length;
     int newLen = oldLen * 2;
     Entry[] newTab = new Entry[newLen];
     int count = 0;

     for (int j = 0; j < oldLen; ++j) {
         Entry e = oldTab[j];
         if (e != null) {
             ThreadLocal<?> k = e.get();
             if (k == null) {
                 e.value = null; // Help the GC
             } else {
                 int h = k.threadLocalHashCode & (newLen - 1);
                 while (newTab[h] != null)
                     h = nextIndex(h, newLen);
                 newTab[h] = e;
                 count++;
             }
         }
     }

     setThreshold(newLen);
     size = count;
     table = newTab;
 }

GET

ThreadLocal的get方法就是调用了ThreadLocalMap的getEntry方法

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

Remove

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

参考