一、什么是ThreadLocal
java.lang.ThreadLocal,线程本地变量,也叫线程局部变量。。通过ThreadLocal存取的数据,总是与当前线程相关,也就是说,JVM 为每个运行的线程,绑定了私有的本地实例存取空间,从而为多线程环境常出现的并发访问问题提供了一种线程隔离机制。对于多线程资源共享的问题,同步机制采用了“以时间换空间”的方式,而ThreadLocal采用了“以空间换时间”的方式。
实际开发中真正使用ThreadLocal的场景较少,大多数使用都是在框架里面。最常见的使用场景可以用它来解决数据库连接、Session管理等保证每一个线程中使用的数据库连接是同一个。
二、API简单使用
public class ThreadLocalMain {
/**
* 根据Java Doc的建议,ThreadLocal一般声明为static变量(被static修
*饰生命周期延长,可以杜绝Entry继承弱引用WeakReference带来的在垃
*圾回收时会直接删除掉key ThreadLocal对象。但是需要注意的是线程运
*行时间过长,对象过大如果不回收有可能会造成内存溢出。需要及时remove()掉.)
* withInitial方法,初始化一个有值的threadLocal.
*/
static ThreadLocal<String> threadLocal = ThreadLocal.withInitial(() -> "调用withInitial初始化一个值");
public static void main(String[] args) {
System.out.println(Thread.currentThread().getName() + " :" + threadLocal.get());
Runnable r1 = () -> {
String name = Thread.currentThread().getName();
threadLocal.set(name + "调用set方法");
System.out.println("------>" + threadLocal.get());
};
Runnable r2 = () -> {
// 如果初始化ThreadLocal时没有使用withInitial初始化初始值,那么在get()时会返回空。
String initialVal = threadLocal.get();
System.out.println("initialVal----------->" + initialVal);
String name = Thread.currentThread().getName();
// 线程thread-local-2 执行set方法.
threadLocal.set(name + "调用set方法!");
// thread-local-2 get值为当前线程set的值.
System.out.println("------>" + threadLocal.get());
// 移除ThreadLocal变量
threadLocal.remove();
// threadLocal.get()值为初始化值.
System.out.println("remove:" + threadLocal.get());
};
Thread thread = new Thread(r1, "thread-local-1");
Thread thread2 = new Thread(r2, "thread-local-2");
thread.start();
thread2.start();
}
}
**********************************************************
main :调用withInitial初始化一个值
------>thread-local-1调用set方法
initialVal----------->调用withInitial初始化一个值
------>thread-local-2调用set方法!
remove:调用withInitial初始化一个值
三、API源码解析
public T get() {
// 获取当前线程对象
Thread t = Thread.currentThread();
//获取此线程对象中维护的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
// 如果此map存在
if (map != null) {
// 以当前的ThreadLocal 为 key,调用getEntry获取对应的存储实体e
ThreadLocalMap.Entry e = map.getEntry(this);
// 找到对应的存储实体 e
if (e != null) {
@SuppressWarnings("unchecked")
// 获取存储实体 e 对应的 value值
// 即为我们想要的当前线程对应此ThreadLocal的值
T result = (T)e.value;
return result;
}
}
// 如果map不存在,则证明此线程没有维护的ThreadLocalMap对象
// 调用setInitialValue进行初始化
return setInitialValue();①
}
/**
* 获取当前线程Thread对应维护的ThreadLocalMap
*
* @param t the current thread 当前线程
* @return the map 对应维护的ThreadLocalMap
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
private T setInitialValue() {
// 调用initialValue获取初始化的值
T value = initialValue();
// 获取当前线程对象
Thread t = Thread.currentThread();
// 获取此线程对象中维护的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
// 如果此map存在
if (map != null)
// 存在则调用map.set设置此实体entry
map.set(this, value);
else
// 1)当前线程Thread 不存在ThreadLocalMap对象
// 2)则调用createMap进行ThreadLocalMap对象的初始化
// 3)并将此实体entry作为第一个值存放至ThreadLocalMap中
createMap(t, value);
return value;
}
protected T initialValue() {
return null;
}
/**
*创建当前线程Thread对应维护的ThreadLocalMap
*
* @param t 当前线程
* @param firstValue 存放到map中第一个entry的值
*/
void createMap(Thread t, T firstValue) {
//这里的this是调用此方法的threadLocal
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
/**
* 设置当前线程对应的ThreadLocal的值
*
* @param value 将要保存在当前线程对应的ThreadLocal的值
*/
public void set(T value) {
// 获取当前线程对象
Thread t = Thread.currentThread();
// 获取此线程对象中维护的ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
// 如果此map存在
if (map != null)
// 存在则调用map.set设置此实体entry
map.set(this, value);
else
// 1)当前线程Thread 不存在ThreadLocalMap对象
// 2)则调用createMap进行ThreadLocalMap对象的初始化
// 3)并将此实体entry作为第一个值存放至ThreadLocalMap中
createMap(t, value);
}
/** ThreadLocal的set最终调用了ThreadLocalMap的set方法 **/
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
int i = key.threadLocalHashCode & (len-1);
//哈希码和数组长度求元素放置的位置,即数组下标 从i开始往后一直遍历到数组最后一个Entry
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//如果key相等,覆盖value
if (k == key) {
e.value = value;
return;
}
//如果key为null,用新key、value覆盖,同时清理历史key=null的陈旧数据
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
tab[i] = new Entry(key, value);
int sz = ++size;
//如果超过阀值,就需要再哈希了
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
通过源码我们知道不管是set、get、remove操作的都是ThreadLocalMap,key=ThreadLocal对象,value=线程局部变量缓存值。getMap最终调用的Thread的成员变量 ThreadLocal.ThreadLocalMap threadLocals.
public class Thread implements Runnable {
/* 与此线程有关的 ThreadLocal 值,该映射由 ThreadLocal 类维护*/
ThreadLocal.ThreadLocalMap threadLocals = null;
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
public class ThreadLocal<T> {
/**ThreadLocalMap是ThreadLocal的一个内部类,ThreadLocalMap是一个定制的哈希映射,
仅适用于维护线程本地值。ThreadLocalMap类是包私有的,允许在Thread类中声明字段。为了帮助
处理非常大且长时间的使用,哈希表entry使用了对键的弱引用。有助于GC回收。 **/
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
}
}