0%

ThreadLocal源码学习

前言:ThreadLocal源码学习

简介

ThreadLocal采用线程隔离的方式存放数据(存放的值是线程封闭,线程间互斥),可以避免多线程之间出现数据访问冲突。

对于同一个static ThreadLocal,不同线程只能从中get,set,remove自己的变量,而不会影响其他线程的变量。

所以ThreadLocal重要作用在于线程内(单个线程内部)的数据共享,保证各个线程间数据安全,每个线程的数据不会被另外线程访问和破坏,多线程间数据隔离

PS:个人建议先学习ThreadLocal的静态内部类ThreadLocalMap,然后再来看ThreadLocal,ThreadLocal比较简单,复杂的地方都在ThreadLocalMap 常用方法

  • ThreadLocal.get: 以ThreadLocal为key获取数据。
  • ThreadLocal.set: 以ThreadLocal为key存放数据
  • ThreadLocal.remove: 以ThreadLocal为key删除数据。
  • ThreadLocal.initialValue:

对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式,使用其内部静态类ThreadLocalMap存储线程的变量

类图

image-20200114235514208

字段

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
/**
* 连续生成的哈希码之间的差异,ThreadLocal实例都会getAndAdd(0x61c88647)。
* 为什么要这么做?因为ThreadLocal储存数据使用ThreadLocalMap,是一个哈希表(散列),为了提高哈希的性能,需要将元素尽可能分散,而这个值0x61c88647,经过实践,发现能使散列值最优分布
*/
private static final int HASH_INCREMENT = 0x61c88647;

/**
* 下一个hashcode,原子更新,初始为0
*/
private static AtomicInteger nextHashCode =
new AtomicInteger();

/**
* 每个线程的hashcode,用于计算散列值,学习ThreadLocalMap源码可以发现,map底层是数组实现
* threadLocalHashCode与数组大小取模就可得到相应下标,在该下标处存储数据
*/
private final int threadLocalHashCode = nextHashCode();

SuppliedThreadLocal

SuppliedThreadLocal是ThreadLocal的一个扩展,用来支持java8函数式接口等新特性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
static final class SuppliedThreadLocal<T> extends ThreadLocal<T> {
/**
* Supplier 是java8新增接口,函数式接口
*/
private final Supplier<? extends T> supplier;

SuppliedThreadLocal(Supplier<? extends T> supplier) {
this.supplier = Objects.requireNonNull(supplier);
}

/**
* Supplier的get方法用于创建对象
*/
@Override
protected T initialValue() {
return supplier.get();
}
}

ThreadLocalMap

因为内容较多,详情见本站ThreadLocalMap源码学习一文

构造方法

默认构造方法,仅创建一个TreadLocal

1
2
public ThreadLocal() {
}

childValue

childValue方法在子类InheritableThreadLocal中定义

1
2
3
T childValue(T parentValue) {
throw new UnsupportedOperationException();
}

createInheritedMap

根据传入的Map创建ThreadLocalMap

1
2
3
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}

createMap

set、setInitialValue方法中被调用,创建ThreadLocalMap类对象并传入firstValue来初始化

1
2
3
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}

get

常用方法,返回当前线程副本中的值

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
public T get() {
// 获取当前线程
Thread t = Thread.currentThread();
// 根据当前线程的ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 判断map
if (map != null) {
// 从map获取键值对
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// 如果map==null,调用setInitialValue进行初始化
return setInitialValue();
}

getMap

返回与当前线程关联的ThreadLocalMap,threadLocals是Thread的属性

1
2
3
ThreadLocalMap (Thread t) {
return t.threadLocals;
}

initialValue

初始化,可以重写initialValue()来设置初始value值

1
2
3
protected T initialValue() {
return null;
}

nextHashCode

计算下一个hashcode

1
2
3
private static int nextHashCode() {
return nextHashCode.getAndAdd(HASH_INCREMENT);
}

remove

删除ThreadLocal中该线程

1
2
3
4
5
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}

set

设置此线程的数据,注意set传的参数是this,即当前ThreadLocal对象,所以说ThreadLocalMap是以ThreadLocal对象为key

1
2
3
4
5
6
7
8
9
10
11
public void set(T value) {
// 获取当前线程
Thread t = Thread.currentThread();
// 获取ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 如果ThreadLocalMap!=null就设置数据,==null则调用createMap创建map
if (map != null)
map.set(this, value);
else
createMap(t, value);
}

setInitialValue

初始化,由get方法调用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
private T setInitialValue() {
// 初始化线程值,默认为null,可以重写initialValue()来设置初始value值
T value = initialValue();
// 获取当前线程
Thread t = Thread.currentThread();
// 获取ThreadLocalMap
ThreadLocalMap map = getMap(t);
// 如果ThreadLocalMap!=null就设置数据,==null则调用createMap创建map
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}

withInitial

用于支持java8函数式编程

1
2
3
public static <S> ThreadLocal<S> withInitial(Supplier<? extends S> supplier) {
return new SuppliedThreadLocal<>(supplier);
}

简单实例

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
public class ThreadLocalTest {
// ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用。每个线程独自拥有一个变量,非共享。 
private static final ThreadLocal<Object> threadLocal = new ThreadLocal<Object>();
private static final int ThreadNum = 3;

public static void main(String[] args) {
Integer integer = new Integer("0");
// ThreadLocal.set方法设置mian线程的变量
ThreadLocalTest.threadLocal.set(integer);
if (ThreadLocalTest.threadLocal.get() != null) {
System.out.println("主线程 : " + ThreadLocalTest.threadLocal.get());
for (int i = 0; i < ThreadNum; i++) {
String name = "ThreadTask" + i;
new Thread(new MyThreadTask(name)).start();
}
}

}

public static class MyThreadTask implements Runnable {
private String name;

MyThreadTask(String name) {
this.name = name;
}

@Override
public void run() {
Integer integer = new Integer("1");
// ThreadLocal.set方法设置子线程的变量
ThreadLocalTest.threadLocal.set(integer);
for (int i = 0; i < 5; i++) {
// ThreadLocal.get方法获取线程变量
System.out.println("线程" + name + ": " + ((Integer) ThreadLocalTest.threadLocal.get() + i));
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}

}
}

image-20200113160654664

可以看到各线程中的变量都是相互独立、互不影响

总结

Thread、ThreadLocal、ThreadLocalMap三者关系如下图

image-20200114235549226

线程调用ThreadLocal对象get、set,第一次调用set时会创建一个ThreadLocalMap对象,注意这个对象是属于线程的。然后以ThreadLocal对象为key存储数据,这里也要注意

-------------本文结束感谢您的阅读-------------