一直都知道有ThreadLocal这么一个类,用来在多线程编程中为每一个Thread保存一份独立的数据,但是一直没有研究为什么这么来实现。今天看了下源码,发现跟之前了解的基本上差不多。
ThreadLocal对象针对每一个Thread有一份独立的数据,实际上就是为每一个Thread创建了一个Map对象,这个Map对象中存储这个线程中所有设置的数据。
ThreadLocal有四个比较关键的方法:
T initialValue() :返回初始化该变量的值,在get()的时候,如果没有设置过值,调用该方法初始化并返回。默认返回null,用于子类覆盖。
T get() :获取该变量当前值
void set(T) :设置该变量的值
void remove() :删除该变量的值
下边是get及相关方法源码:
public T get() {
Thread t = Thread.currentThread(); //获取当前线程
ThreadLocalMap map = getMap(t); //根据当前线程获取值map,其实就是返回了Thread内部的一个map。
if (map != null) { //如果已存在map,获取,并返回当前ThreadLocal对象对应值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null)
return (T)e.value;
}
return setInitialValue(); //如果不存在map,或者当前ThreadLocal无对应的值,那么返回初始化。
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals; //返回Thread内部的map。
}
private T setInitialValue() {
T value = initialValue(); //调用初始化方法,初始化当前ThreadLocal对应值
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
看上面的源码基本上就可以了解set和remove的内部实现了,再次就不贴了。
从上面的getMap方法可以看出,ThreadLocal并没有在内部去存储线程的数据,而仅仅只是线程内部的数据的一个代理类,这样做可以清晰而又简单的表现出数据对象线程隔离的效果。同时,由于数据存储在线程内部,那么数据会一直伴随着线程直到线程结束,所以可以通过这种方式来在一个线程中进行值传递操作,Spring中的对有状态的Bean的共享就是使用ThreadLocal实现的。
当然,ThreadLocal经常用来解决并发问题,那么与同步机制有什么样的不同呢。
性能上来说,ThreadLocal没有锁的使用,性能上会比同步好很多,而且由于不用关心并发控制,所以编程的复杂度大为降低,但是由于每一个线程都会有一个副本,这样相对于同步机制来说,消耗了大量的内存,总得来说就是一个空间换时间的结果。
ThreadLocal与同步机制其实要解决的并不是同一类问题。同步机制实现了数据共享,用来解决多个线程对同一个资源的访问,同时实现不同线程间的通信。而ThreadLocal实现了数据隔离,用于多个线程的数据共享冲突情况。