TheadLocal称为线程本地存储,就是说一个变量,每个线程都有它的一个副本,并且相互之间是独立的。
ThreadLocal类的实现
下面是该类的提供的关键的几个方法:
public T get() { }
public void set(T value) { }
public void remove() { }
protected T initialValue() { }
通过查看jdk中该类的源码,可以大致看到上述方法的实现,其中:
/**
* Returns the value in the current thread's copy of this
* thread-local variable. If the variable has no value for the
* current thread, it is first initialized to the value returned
* by an invocation of the {@link #initialValue} method.
*
* @return the current thread's value of this thread-local
*/
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
是get()方法的实现,首先是通过一个getMap得到ThreadLocalMap,然后在这个ThreadLocalMap中通过this来获得对应的值作为返回值:如果将ThreadLocalMap看作map的话,我们可以发现实际上这个map的key是当前的ThreadLoca变量。
接下来看getMap方法的实现
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
实际上就是返回了线程t的成员变量threadLocals,该变量是一个ThreadLocalMap类型。
这样我们就可以总结出TheadLocal大致的工作原理:有依附于每个线程的一个ThreadLocalMap的类似map的数据结构,这个数据结构的key就是我们定义的ThreadLocal变量,同时我们可以定义多个ThreadLocal变量作为这个map的key。
用图形表示为:
从这里来看,ThreadLocal其实只是相当于声明了某种类型是线程本地存储,实际上的数据是存储在每个Thread实例的成员变量中的。这也就是线程本地存储名称的来源—数据实际上存储在本地线程中。
另外还有set:
/**
* Sets the current thread's copy of this thread-local variable
* to the specified value. Most subclasses will have no need to
* override this method, relying solely on the {@link #initialValue}
* method to set the values of thread-locals.
*
* @param value the value to be stored in the current thread's copy of
* this thread-local.
*/
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
用于修改value值。
另外注意到,ThreadLocalMap中的内部类Entry是声明为WeakReference,这是java中的四种Reference类型之一。通过相关相关书籍和网页,简单介绍一下:
StrongReference,就是我们通常通过new Object()这样的形式得到的引用,叫强引用,只要强引用还在,GC就不会回收相应的对象。
SoftReference,软引用,系统在内存将要溢出之前,会对这类引用的对象进行清理。比如外部数据源:文件、数据库、网络等可声明为该类型引用。
WeakReference,弱引用,被弱引用关联的对象如果被JVM识别为弱可达,那么在下次GC的时候就会进行回收。
SoftReference与WeakReference的区别是GC的时间是尽量推迟的。
Phantom,虚引用。不能通过虚引用来获得一个对象实例,唯一的目的是在对象被回收时得到一个系统通知。
ThreadLocal应用场景
数据库连接、Session管理等