为什么用ThreadLocal

在多线程的环境下,每个线程都有自己的数据。线程使用局部变量肯定比使用全局变量好。使用全局变量,一不小心就会引发线程安全问题,全局变量的修改必须加锁。而局部变量只有线程自己能看见,不会影响其他线程。但是局部变量有个缺点,如果别的函数也要使用,在函数调用的时候,需要通过参数的方式进行层层传递。
有没有更简单的方式,不需要传递,也能解决多线程安全问题。ThreadLocal为此而生。

什么是ThreadLocal

当使用ThreadLocal维护变量时,ThreadLocal为每个使用该变量的线程提供独立的变量副本,所以每一个线程都可以独立地改变自己的副本,而不会影响其它线程所对应的副本。

例子

初始化15个线程,每个线程都有自己的map,往map中写入20个值。

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
38
39
40
41
42
public class ThreadLocalTest implements Runnable {
int id;
private static final ThreadLocal<HashMap> threadLocal = new ThreadLocal<HashMap>() {
@Override
protected HashMap initialValue() {
return new HashMap();
}
};

public ThreadLocalTest(int id) {
this.id = id;
}

public void run() {
Map map=threadLocal.get();
for (int i = 0; i < 20; i++) {
map.put(i, i + id * 100);
try {
Thread.sleep(100);
} catch (Exception e) {
}
}
printMap();
}

public void printMap(){//不需要传递参数
Map map=threadLocal.get();
System.out.println(Thread.currentThread().getName() + "#map.size()" + map.size() + "#" + map);
}

public static void main(String[] args) {
Thread[] runs = new Thread[15];
ThreadLocalTest runnableTest = new ThreadLocalTest(1);
for (int i = 0; i < runs.length; i++) {
runs[i] = new Thread(runnableTest);
}
for (int i = 0; i < runs.length; i++) {
runs[i].start();
}
}

}

源码分析

核心类&核心方法

1.ThreadLocalMap
2.T get() 获取当前线程的副本变量值
3.void set(T value) 保存当前线程的副本变量值
4.void remove() 移除当前前程的副本变量值

ThreadLocalMap

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
38
39
40
41
42
43
static class ThreadLocalMap {//ThreadLocal的内部静态类,

/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {//Entry继承WeakReference,它将以弱引用的方式被持有。
//内部静态类以及弱引用都是防止内存泄漏的有效方式。
/** The value associated with this ThreadLocal. */
Object value;

Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
......
//构造方法,初始化相关信息
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
//threadLocalHashCode&(INITIAL_CAPACITY - 1),目的是让i小于数组长度。如果每个线程只存一个变量,那么一个key都是一样的。多个变量ThreadLocal变多,要考虑到初始长度。
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
......
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);
//如果插入的过程中,产生冲突,这里解决Hash冲突的方式不是链表的方式,而是采用线性探测的方式,根据初始key的hashcode值确定元素在table数组中的位置,如果发现这个位置上已经有其他key值的元素被占用,则利用固定的算法寻找一定步长的下个位置,依次判断,直至找到能够存放的位置。

}

}

ThreadLocal核心方法

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
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
public class ThreadLocal<T> {
public void set(T value) {
Thread t = Thread.currentThread();//获取当前线程
ThreadLocalMap map = getMap(t);//返回的是thread.threadLocals
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

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

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

public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//获取Entry
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;//key是ThreadLocal的hashcode,value是要维护的值
return result;
}
}
return setInitialValue();
}

/**
* 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();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}


}

参考

1.ThreadLocal-面试必问深度解析
2.threadlocal使用