ThreadLocal源码解析

ThreadLocal源码解析

多线程访问同一个共享变量时特别容易出现并发问题,特别是在多个线程需要对一个共享变量进行写入时。为了保证线程安全,一般使用者在访问共享变量时需要进行适当的同步。同步的措施一般是加锁,这就需要使用者对锁有一定的了解,这显然加重了使用者的负担。

JDK包提供了ThreadLocal,它提供了线程本地变量,也就是说,如果创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个本地副本。当多个线程操作这个变量时,实际操作的是自己本地内存里面的变量,从而避免了线程安全问题。创建一个ThreadLocal变量后,每个线程都会复制一个变量到自己的本地内存。如下图:

ThreadLocal简单例子

下面看一个简单的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
public class ThreadLocalTest {
private static final ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>() {
// 重写初始化值方法
@Override
protected Integer initialValue() {
return Integer.valueOf(0);
}
};

public static void main(String[] args) {
Thread[] threads = new Thread[5];
for (int i = 0; i < 5; i++) {
threads[i] = new Thread(new Runnable() {
@Override
public void run() {
Random random = new Random();
System.out.println(Thread.currentThread() + "'s initial value: " + threadLocal.get());
for (int j = 0; j < 5; j++) {
threadLocal.set(threadLocal.get() + random.nextInt(20));
}
System.out.println(Thread.currentThread() + "'s last value: " + threadLocal.get());
}
});
}

for (Thread t : threads)
t.start();
}
}

从上图的运行结果可以看出,它们的值都是线程独有的,互相不影响。

ThreadLocal实现原理

首先看一下ThreadLocal相关类的类图结构

由该图可知,Thread类中有一个threadLocals和inheritableThreadLocals变量,它们都是ThreadLocalMap类型。而ThreadLocalMap是一个定制化的HashMap。

它们默认值为null

1
2
3
4
5
6
7
8
9
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

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

只有当前线程第一次调用ThreadLocal的set或者get方法时才会创建它们。起始每个线程的本地变量不是存放在ThreadLocal实例里面,而是存放在调用线程的threadLocals变量里面。ThreadLocal类型的本地变量存放在具体的线程内存空间中。ThreadLocal是一个工具壳,它通过set方法把value值放入线程的threadLocals变量里面将其拿出来使用,所以当不需要使用本地变量时可以通过调用ThreadLocal变量的remove方法,从当前线程的threadLocals里面删除该本地变量。

TheadLocal主要方法

set

首先看下源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
/**
* 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();
// 当前线程的引用为key,获取对应的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
if (map != null)
// 如果map不为空,设置值
map.set(this, value);
else
// 创建新map
createMap(t, value);
}

整体的流程就是:获取当前线程的引用,然后调用getMap方法,获取threadLocalMap的实例,接着设置值。

getMap

接下来看下getMap方法

1
2
3
4
5
6
7
8
9
10
/**
* 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;
}

getMap(t)的作用是获取线程自己的变量threadLocals,threadLocal变量被绑定到了线程的成员变量上。

get

下面看下get方法

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/**
* 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实例
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();
}

流程大致是:获取当前线程的引用,继而获取该线程的threadLocalMap实例,接着从map中获取对应线程的值,如果有的话。否则就调用setInitialValue()方法,设置初始值。

setInitialValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* 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;
}

如果当前线程的threadLocals变量不为空,则设置当前线程的本地变量值为null,否则调用createMap方法创建当前线程的createMap变量。

initialValue方法可以被重写,默认为返回null。

createMap方法会创建一个新的ThreadLocalMap对象。

1
2
3
4
5
6
7
8
9
10
/**
* 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);
}

remove

remove方法实现为:如果当前线程的threadLocals变量不为空,则删除当前线程中执行ThreadLocal实例的本地变量。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
/**
* Removes the current thread's value for this thread-local
* variable. If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim. This may result in multiple invocations of the
* {@code initialValue} method in the current thread.
*
* @since 1.5
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}

总的来说,在每个线程内部都有一个名为threadLocals的成员变量,该变量的类型为HashMap,其中key为我们定义的ThreadLocal变量的this引用,value则为我们使用set方法设置的值。每个线程的本地变量存放在线程自己的内存变量threadLocals中,如果当前线程一直不消亡,那么这些本地变量会一直存在,所以可能会造成内存溢出,因此使用完毕后要记得调用ThreadLocal的remove方法深处对应线程的threadLocals中的本地变量。

ThreadLocal不支持继承性

看下下面这个例子

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public class ThreadLocalTest {
// 创建线程变量
public static ThreadLocal<String> threadLocal = new ThreadLocal<>();

public static void main(String[] args) {
// 设置线程变量
threadLocal.set("hello world");
// 启动子线程
Thread thread = new Thread(new Runnable() {

@Override
public void run() {
// TODO Auto-generated method stub
// 子线程输出线程变量的值
System.out.println("thread: " + threadLocal.get());
}
});
thread.start();

// 主线程输出线程变量的值
System.out.println("main: " + threadLocal.get());
}
}

它的结果是:

也就是说,同一个ThreadLocal变量在父线程中被设置值后,在子线程中是获取不到的。因为子线程thread里面调用get方法时当前线程为thread线程,而这里调用set方法设置线程变量的是main线程,两者是不同的线程,自然子线程访问时返回null。

InheritableThreadLocal类

InheritableThreadLocal类继承自ThreadLocal,其提供了一个特性,就是让子线程可以访问在父线程中设置的本地变量。

InheritableHereadLocal继承了ThreadLocal类,并重写了getMap、createMap和childValue三个方法。

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
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
/**
* Computes the child's initial value for this inheritable thread-local
* variable as a function of the parent's value at the time the child
* thread is created. This method is called from within the parent
* thread before the child is started.
* <p>
* This method merely returns its input argument, and should be overridden
* if a different behavior is desired.
*
* @param parentValue the parent thread's value
* @return the child thread's initial value
*/
protected T childValue(T parentValue) {
return parentValue;
}

/**
* Get the map associated with a ThreadLocal.
*
* @param t the current thread
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}

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

getMap

1
2
3
4
5
6
7
8
/**
* Get the map associated with a ThreadLocal.
*
* @param t the current thread
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}

当调用getMap方法获取ThreadLocal变量时,不再返回threadLocals,而是返回inheritableThreadLocals变量。

createMap

1
2
3
4
5
6
7
8
9
/**
* Create the map associated with a ThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the table.
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}

当第一次调用set方法时,创建的是当前的inheritableThreadLocals变量的实例,而不是threadLocals。

childValue

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
/**
* Computes the child's initial value for this inheritable thread-local
* variable as a function of the parent's value at the time the child
* thread is created. This method is called from within the parent
* thread before the child is started.
* <p>
* This method merely returns its input argument, and should be overridden
* if a different behavior is desired.
*
* @param parentValue the parent thread's value
* @return the child thread's initial value
*/
protected T childValue(T parentValue) {
return parentValue;
}

childValue方法的应用场景比较复杂,得从创建线程的时候说起。

在创建Thread时候,有这么几句:

1
2
3
4
5
6
7
8
9
10
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
Thread parent = currentThread();
...
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
...
}

在创建线程的时候,构造函数会调用init方法,第4行先获取了当前线程的引用,作为父线程,然后判断主线程的inheritThreadLocals的值是否为null,不为null的话就调用createInheritedMap方法,传入父线程的inheritableThreadLocals参数。

1
2
3
4
5
6
7
8
9
10
/**
* Factory method to create map of inherited thread locals.
* Designed to be called only from Thread constructor.
*
* @param parentMap the map associated with parent thread
* @return a map containing the parent's inheritable bindings
*/
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}

接下来看下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
/**
* Construct a new map including all Inheritable ThreadLocals
* from given parent map. Called only by createInheritedMap.
*
* @param parentMap the map associated with parent thread.
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];

for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}

在17行那句代码,会调用重写的childValue方法,将父线程的值,复制到子线程的ThreadLocalMap对象中。

因此,只要把ThreadLocal不支持继承性章节中的例子改为:

public static ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();

0%