概念
1、ThreadLocal是一种变量类型,我们称之为“线程局部变量”。每个线程访问这种变量的时候都会创建该变量的副本,这个变量副本为线程私有;ThreadLocal类型的变量一般用private static加以修饰。
2、ThreadLocal中的常用方法:
ThreadLocal.get: 用来获取ThreadLocal在当前线程中保存的变量副本;
ThreadLocal.initialValue: ThreadLocal没有被当前线程赋值时或当前线程刚调用remove方法后调用get方法,返回此方法值;
ThreadLocal.set: 设置ThreadLocal中当前线程共享变量的值;
ThreadLocal.remove: 移除ThreadLocal中当前线程共享变量的值。
1)get方法
public class ThreadLocal<T> {
public T get() {
Thread var1 = Thread.currentThread(); //取得当前线程
ThreadLocal.ThreadLocalMap var2 = this.getMap(var1);
if (var2 != null) {
ThreadLocal.ThreadLocalMap.Entry var3 = var2.getEntry(this); //获取到<key,value>键值对
if (var3 != null) {
Object var4 = var3.value;
return var4; //如果获取成功,则返回value值
}
}
return this.setInitialValue(); //如果map为空,则调用setInitialValue方法返回value
}
}
通过getMap方法获取当前线程中的所有ThreadLocal< T >记录threadLocals;先判断是否已有值,如果没有则调用setInitialvalue()赋值
ThreadLocalMap是ThreadLocal的内部类:
public class ThreadLocal<T> {
static class ThreadLocalMap {
}
}
getMap方法:调用当期线程var1,返回当前线程var1中的一个成员变量threadLocals
ThreadLocal.ThreadLocalMap getMap(Thread var1) {
return var1.threadLocals;
}
然后再查看Thread.threadLocals的值(在Thread类中):
public class Thread implements Runnable {
ThreadLocalMap threadLocals = null;
}
发现threadLocals就是一个ThreadLocalMap;
2)setInitialvalue方法
public class ThreadLocal<T> {
private T setInitialValue() {
Object var1 = this.initialValue();
Thread var2 = Thread.currentThread();
ThreadLocal.ThreadLocalMap var3 = this.getMap(var2);
if (var3 != null) { //如果map不为空,就设置键值对
var3.set(this, var1);
} else {
this.createMap(var2, var1); //为空,再创建Map
}
return var1;
}
}
===>查看createMap方法:
void createMap(Thread var1, T var2) {
var1.threadLocals = new ThreadLocal.ThreadLocalMap(this, var2);
}
====》ThreadLocal创建副本完成。
每个线程Thread内部有一个ThreadLocal.ThreadLocalMap类型的成员变量threadLocals,这个threadLocals就是用来存储实际的变量副本的,键值为当前ThreadLocal变量,value为变量副本;
初始时,在Thread里面,threadLocals为空,当通过ThreadLocal变量调用get()方法或者set()方法,就会对Thread类中的threadLocals进行初始化,并且以当前ThreadLocal变量为键值,以ThreadLocal要保存的副本变量为value,存到threadLocals。
然后在当前线程里面,如果要使用副本变量,就可以通过get方法在threadLocals里面查找。
3)set方法
public class ThreadLocal<T> {
public void set(T var1) {
Thread var2 = Thread.currentThread();
ThreadLocal.ThreadLocalMap var3 = this.getMap(var2);
if (var3 != null) {
var3.set(this, var1);
} else {
this.createMap(var2, var1);
}
}
}
先判断是否有值了,如果有则覆盖,没有则创建新的Map对象。
4)remove方法
public class ThreadLocal<T> {
public void set(T var1) {
Thread var2 = Thread.currentThread();
ThreadLocal.ThreadLocalMap var3 = this.getMap(var2);
if (var3 != null) {
var3.set(this, var1);
} else {
this.createMap(var2, var1);
}
}
}
Thread通过ThreadLocalMap来存储线程中的变量,第一次获取时需要先通过set()将值保存进map中。通过泛型来区分给个变量的key。
示例:
public class Test {
ThreadLocal<Long> longLocal = new ThreadLocal<Long>();
ThreadLocal<String> stringLocal = new ThreadLocal<String>();
public void set() {
longLocal.set(Thread.currentThread().getId());
stringLocal.set(Thread.currentThread().getName());
}
public long getLong() {
return longLocal.get();
}
public String getString() {
return stringLocal.get();
}
public static void main(String[] args) throws InterruptedException {
final Test test = new Test();
test.set();
System.out.println(test.getLong());
System.out.println(test.getString());
Thread thread1 = new Thread(){
public void run() {
test.set();
System.out.println(test.getLong());
System.out.println(test.getString());
};
};
thread1.start();
thread1.join();
System.out.println(test.getLong());
System.out.println(test.getString());
}
}
分析:
a)main线程中和thread1线程中,longLocal保存的副本值和stringLocal保存的副本值都不一样;
b)实际通过ThreadLocal创建的副本是存储在每个线程自己的threadLocals中的;
c)为何threadLocals的类型ThreadLocalMap的键值为ThreadLocal对象,因为每个线程中可有多个threadLocal变量,就像上面代码中的longLocal和stringLocal;
d)在进行get之前,必须先set,否则会报空指针异常; ;
e)如果想在get之前不需要调用set就能正常访问的话,必须重写initialValue()方法。
示例 - 不用先set而直接调用get:
public class Test {
ThreadLocal<Long> longLocal = new ThreadLocal<Long>(){
protected Long initialValue() {
return Thread.currentThread().getId();
};
};
ThreadLocal<String> stringLocal = new ThreadLocal<String>(){;
protected String initialValue() {
return Thread.currentThread().getName();
};
};
public void set() {
longLocal.set(Thread.currentThread().getId());
stringLocal.set(Thread.currentThread().getName());
}
public long getLong() {
return longLocal.get();
}
public String getString() {
return stringLocal.get();
}
public static void main(String[] args) throws InterruptedException {
final Test test = new Test();
test.set();
System.out.println(test.getLong());
System.out.println(test.getString());
Thread thread1 = new Thread(){
public void run() {
test.set();
System.out.println(test.getLong());
System.out.println(test.getString());
};
};
thread1.start();
thread1.join();
System.out.println(test.getLong());
System.out.println(test.getString());
}
}
3、使用完线程共享变量后,显示调用ThreadLocalMap.remove方法清除线程共享变量;