ThreadLocal的作用:
使同一个线程共享一个实例,不同线程使用各自的实例,互不影响
ThreadLocal比较正宗的应用解释:
每个线程都将拥有一份属于自己的变量,只要Thread没结束,随时都能取出其内部的ThreadLocalMap,然后结合ThreadLocal自身,就可以拿到Map中存放的变量。
为什么要这样?解决了方法链中传递同一个参数的麻烦!
应用场景:
一个线程在运行,需要执行很多类的不同方法,这些方法都使用到了一个变量
那么就得不断的把这个变量往后传递,很麻烦
如何在Thread的整个生命周期中都能使用这个变量呢,使用ThreadLocalMap来保存。
每个Thread中都有一个属性变量:
ThreadLocal.ThreadLocalMap threadLocals
ThreadLocal中定义了一个内部类(当做一个Map):
ThreadLocalMap map
这个Map比较特殊:
key - ThreadLocal
value - 我们要传递的变量
Thread类中的这个Map是怎么初始化的呢?
创建ThreadLocal对象,当调用其get方法获取变量时,如果从Thread中取到的Map为空,
则创建一个新的Map,并会将引用赋值给Thread中的那个threadLocals
这样,Thead中就持有了一份Map的引用
任何时候只要该线程还在运行,都可以通过currentThread得到它自己的ThreadLocalMap
然后,以ThreadLocal为key,即可从Map中取出其中存放的变量、
在设计上,对于开发人员来讲,Thread和ThreadLocalMap都隐藏在了背后
而是通过直接操作ThreadLocal来完成变量的存入,获取,删除操作的!
最好要理解背后隐藏的一些东西,这样才能深刻理解运行原理。
源码:
Thread类持有ThreadLocal的引用
public class Thread implements Runnable {
...
ThreadLocal.ThreadLocalMap threadLocals = null;
...
}
ThreadLocal类中的主要方法
public T get() {//threadLocal对象获取其内部Map中存放的value
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//map的key:线程自身this
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;//该value即在不同方法中被使用到的变量/对象
}
return setInitialValue();
}
获取当前Thread中的ThreadMap集合
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
设置变量到ThreadMap中
如果map为空,则初始化ThreadMap再进行保存
最后将变量返回
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;
}
获取初始化变量
该方法为protected权限,在创建ThreadLocal对象时,可以复写此方法,达到初始化的目的
所谓初始化,即:将变量传入到ThreadLocal中,存放到ThreadLocalMap的value中
protected T initialValue() {
return null;
}
比如,复写初始化方法
创建ThreadLocalMap,将Map对象的引用传递给Thread的成员变量threadLocals
这样,即实现了ThreadLocal保存对象。
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
获取ThreadLocal中存放的变量:
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);//获取Thread中TheadLocalMap
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//以ThreadLocal自己为key获取value
if (e != null)
return (T)e.value;
}
return setInitialValue();
}
拿到Thread中的ThreadLocalMap
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
==================================================================================
一个小例子,不是很好
import java.text.SimpleDateFormat;
import java.util.Date;
public class DateUtil {
private static final String DATA_FORMAT = "yyyy-MM-dd HH:mm:ss";
private static ThreadLocal threadLocal = new ThreadLocal() {
//第三步:调用重写了的initialValue(),获取到将该方法的返回值
//以currentThread为key获取ThreadLocalMap
//如果为空,则创建Map。key为ThreadLocal对象,value为初始化方法的返回值
//并将Map的引用赋给Thread中的ThreadLocalMap【以后直接通过currentThread就能取出Map来,然后从Map中就拿到变量了】
@Override
protected SimpleDateFormat initialValue() {
return new SimpleDateFormat(DATA_FORMAT);
}
};
public static SimpleDateFormat getDateFormat() {
//第二步:threadLocal对象调用get()
//get()中调用getMap(t),t为currentThread,即获取Thread类中的属性threadLocals
//发现ThreadLocal.ThreadLocalMap threadLocals = null;
//开始初始化ThreadLocalMap
//首先,将调用initialValue()初始化方法,如果子类没有复写,则返回默认值null
return threadLocal.get();
}
public static String format(Date date) {
return getDateFormat().format(date);
}
public static void main(String[] args) {
final Date date = new Date();
for(short i=0; i<10000; i++)
//第一步:创建Thread的实例对象t
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(DateUtil.getDateFormat().format(date));
}
}).start();
}
}