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<SimpleDateFormat> threadLocal = new ThreadLocal<SimpleDateFormat>() { //第三步:调用重写了的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(); } }