ThreadLocal提供了线程本地变量,也就是如果你创建了一个ThreadLocal变量,那么访问这个变量的每个线程其实都会有此变量的一个本地副本。所以多线程访问次变量实际操作的时自己线程本地内存里面的变量。直接上例子:
static ThreadLocal<String> localVariable = new ThreadLocal<>();
public static void main(String[] args) {
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
localVariable.set("I'm variable in threadOne");
System.out.println("threadOne variable:" + localVariable.get());
}
});
Thread threadTwo = new Thread(new Runnable() {
@Override
public void run() {
localVariable.set("I'm variable in threadTwo");
System.out.println("threadTwo variable:" + localVariable.get());
}
});
threadOne.start();
threadTwo.start();
}
执行结果为如下。说明虽然程序里面是操作的同一个变量localVariable,但是不同线程都有自己的一份拷贝。
threadOne variable:I'm variable in threadOne
threadTwo variable:I'm variable in threadTwo
如图所示为ThreadLocal相关类的类图结构。
从该图中能够看到,Thread类中有一个threadLocals和一个inheritableThreadLocals变量,inheritableThreadLocals和继承有关,本文主要先讲threadLocals实现。他们都是ThreadLocalMap类型。ThreadLocalMap是一个定制化的HashMap,默认情况下,这两个变量都为空,只有第一次调用set或get方法时才会创建他们。能够看出来,其实线程的本地变量没有存在ThreadLocal里面,而是存在了自己的线程内部,只是通过ThreadLocal来访问它们,下面看各方法实现原理的时候能够认清这一点。ThreadLocal被设置成HashMap也是因为一个线程可以存放多个本地变量。
其实现代码如下:
void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(T); //根据当前线程获得它自己的map变量
if (map != null)
map.set(this, value); //以此ThreadLocal变量为key,放入到此线程的value中
else
createMap(t, value); //创建新的ThreadLocalMap
}
代码的逻辑也很清楚,要注意的是,每个线程有一个threadLocals变量,其中每个键值对的键就是某一个ThreadLocal实例对象引用,值就是所对应的变量。例如上面提到的例子中,threadOne的threadLocals变量中就存在这样一个键值对:
createMap的实现为:
void createMap(Thread T, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue); //新创建一个ThreadLocalMap并将键值对放进去
}
其实现代码如下:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this); //使用当前ThreadLocal变量作为key,取得这个Entry
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue(); //threadLocalMaps为空,则初始化当前线程的threadLocals成员变量
}
setInitialValue()方法的实现如下:
private T setInitialValue() {
T value = initialValue(); //初始化值
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) //再一次判断当前Map是不是空
map.set(this, value);
else
createMap(t, value); //本质还是调用了createMap函数
return value;
}
protected T initialValue() {
return null;
}
实现代码如下:
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this); //从map中将当前线程为键的键值对删掉
}
每个线程内部都有一个名为threadLocals的成员变量,该变量类型为HashMap,key为我们定义的ThreadLocal变量的this引用,value则为我们使用set方法设置的值。如图所示:
ThreadLocal是不支持继承性的,所谓继承性也是针对父线程和子线程来说的,例如:
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() {
System.out.println("thread:" + threadLocal.get());
}
});
thread.start();
System.out.println("main:" + threadLocal.get());
}
输出结果如下:能够理解,即使thread是main线程的子线程,但仍然是两个不同的线程,所以各自的threadLocal的值是不一样的。那为了子线程能够直接访问父线程的变量,InheritableThreadLocal类就产生了。
thread:null
main:hello world
InheritableThreadLocal提供了一个特性,就是子线程可以访问在父线程中设置的本地变量,先看一下InheritableThreadLocal的代码:
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
(1)
protected T childValue(T parentValue) {
return parentValue;
}
(2)
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
(3)
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
能看到代码(2)和代码(3)变成了针对于线程中的inheritableThreadLocals变量进行操作的,而不再是threadLocals变量,而本身inheritableThreadLocals也是一个ThreadLocal类型变量。代码(1)有什么用呢?先看一下Thread在创建时发生了什么,即Thread类的默认构造函数:
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name, long stackSize, AccessControlContext acc) {
//获取“当前”线程,也就是创建此线程的线程,或者说叫父线程
Thread parent = currentThread();
//如果父线程的inheritableThreadLocals变量不为null
if (parent.inheritableLocals != null) {
//利用父线程中的inheritableThreadLocals来设置子线程中的inheritableThreadLocals变量
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
this.stackSize = stackSize;
tid = nextThreadID();
}
}
createInheritedMap代码如下,即利用父线程的inheritableThreadLocals变量作为ThreadLocalMap构造函数的参数构造一个新的ThreadLocalMap变量,并返回给子线程(当前线程)的inheritableThreadLocals变量。
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
构造函数代码及所做的工作如下:将父线程inheritableThreadLocals变量中所有的Entry都复制到子线程的inheritableThreadLocals(此处存放到了table中变量中)中。此处用到了代码(1)重写的部分。
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")
//得到父线程中变量对应的key
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
//重写代码(1)的方法
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++;
}
}
}
}
其实从代码(1)本身的定义能够看到,它基本没什么作用,只是形式上利用父线程中的value来获取子线程中的value,但是两者是一个,就是将参数里面的值直接返回,我想在此处要重写成这样,可能是为了以后改变的策略的时候只需要改变这一处就行。比如不想返回和父线程一样的value,而是要加某些操作,那只需修改这个函数,所有其他部分都会修改。