Threadlocal源码分析

为了彻底搞明白ThreadLocal的工作原理,下面会截取代码和画图详细说明.

简单ThreadLocal工作流程

  • 首先搞清楚Thread,ThreadLocal,ThreadLocalMap这三个类的关系.
1
2
3
public class Thread implements Runnable {
    ThreadLocal.ThreadLocalMap threadLocals = null;
}
1
2
3
4
5
public class ThreadLocal<T> {
	static class ThreadLocalMap {

	}
}

上面两段代码截取jdk8源码,Thread对象内部定义了成员变量ThreadLocal.ThreadLocalMap threadLocals = null,ThreadLocalMapThreadLocal的一个静态内部类,三者的代码关系就这么简单.

假定在业务系统中有这样两个service和对应的方法方法.

  • XService1
    1
    2
    3
    4
    5
    6
    7
    8
    public class XService1 {
      ThreadLocal<String> threadLocal1 = new ThreadLocal<String>();
    
      public void testThreadLocalValue() {
          threadLocal1.set("test1");
          String str1 = threadLocal1.get();
      }
    }
    
  • XService2 ``` public class XService2 { ThreadLocal threadLocal2 = new ThreadLocal();

    @Test public void testThreadLocalValue() { threadLocal2.set(“test2”); String str2 = threadLocal2.get(); }

}

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15

按照上图的例子,同时假定有两个线程执行这两个Service对应的方法,可模拟其执行过程.

1. Thead1执行XService1.testThreadLocalValue,此时threadLocal1.set("test1"),在Thead1.threadLocals对象中添加了一个新的Entry其key为threadLocal1,value为"test1";

2. Thead2执行XService1.testThreadLocalValue,此时threadLocal1.set("test1"),在Thead2.threadLocals对象中添加了一个新的Entry其key为threadLocal1,value为"test1";

3. Thead1执行XService2.testThreadLocalValue,此时threadLocal2.set("test2"),在Thead1.threadLocals对象中添加了一个新的Entry其key为threadLocal2,value为"test2";

4. Thead2执行XService2.testThreadLocalValue,此时threadLocal2.set("test2"),在Thead2.threadLocals对象中添加了一个新的Entry其key为threadLocal2,value为"test2";




# 源码分析

//n为当前table有效元素的个数 private boolean cleanSomeSlots(int i, int n) { boolean removed = false; Entry[] tab = table; int len = tab.length; do { //nextIndex方法能够保证从i到i-1遍历整个table i = nextIndex(i, len); Entry e = tab[i];

1
2
3
4
5
6
7
8
9
10
     //Eentry不为空但是key为空的entry对象
    if (e != null && e.get() == null) {
        n = len;
        removed = true;
        i = expungeStaleEntry(i);
    }
    
    //无符号右移位,执行n次
} while ( (n >>>= 1) != 0);
return removed; } ```
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
private int expungeStaleEntry(int staleSlot) {
    Entry[] tab = table;
    int len = tab.length;

    // expunge entry at staleSlot
    tab[staleSlot].value = null;
    tab[staleSlot] = null;
    size--;

    // Rehash until we encounter null
    Entry e;
    int i;
    for (i = nextIndex(staleSlot, len);
         (e = tab[i]) != null;
         i = nextIndex(i, len)) {
        ThreadLocal<?> k = e.get();
        if (k == null) {
            e.value = null;
            tab[i] = null;
            size--;
        } else {
        	//重新计算hash
            int h = k.threadLocalHashCode & (len - 1);
            if (h != i) {
                tab[i] = null;

                // Unlike Knuth 6.4 Algorithm R, we must scan until
                // null because multiple entries could have been stale.
                while (tab[h] != null)
                    h = nextIndex(h, len);
                tab[h] = e;
            }
        }
    }
    return i;
}

Reference