在正式开始之前,从一小段代入手:
public static void main(String[] args) {
ThreadLocal threadLocal = new ThreadLocal();
threadLocal.set(5);
System.out.println(threadLocal.get());
}
源码:ThreadLocal类set()方法
看到源码不要慌,不懂往下翻
public class ThreadLocal<T> {
/**
* 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);
}
}
ThreadLocal类get()方法
public class ThreadLocal<T> {
/**
* 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();
}
}
大家可以尝试断点执行下,大致明白整个执行过程。
我们从这段简单的代码中逐步分析
1. ThreadLocal初始化
ThreadLocal threadLocal = new ThreadLocal();
查看源码:
public ThreadLocal() {
}
可以看到构造函数并没有做任何处理。
2. ThreadLocal设置值
threadLocal.set(5);
获取类型为ThreadLocalMap的map
以当前的threadLocal作为key,我们要存的对象
5作为value存放到map中。
3. ThreadLocal获取值
threadLocal.get()
当前threadLocal作为key,获取map中Entry对象
获取对象的value值
看完上面大致的执行过程,初次接触的宝子们一定还一头雾水
ThreadLocalMap是啥?Entry是啥?
接下来让我们走近源码,一层一层拨开ThreadLocal神秘的面纱
源码剖析
Tip1
首先大家看下ThreadLocal的整个结构
public ThreadLocal() {
static class ThreadLocalMap {
static class Entry extends WeakReference<ThreadLocal<?>> {
}
}
}
一共有3层结构
ThreadLocalMap是ThreadLocal的静态内部类
Entry是ThreadLocalMap的静态内部类
真正存储数据的是ThreadLocalMap类型数据,ThreadLocal只是作为key值。
首先看下ThreadLocalMap是怎么实现存储的
static class ThreadLocalMap {
// Entry继承 WeakReference弱引用
static class Entry extends WeakReference<ThreadLocal<?>> {
// 实际存储的对象value
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
// 初始化容量
private static final int INITIAL_CAPACITY = 16;
// Entry类型的数组
private Entry[] table;
// 大小
private int size = 0;
}
不理解没关系,我们来用实际代码演练下:
public static void main(String[] args) {
ThreadLocal<String> threadLocal = new ThreadLocal<>();
threadLocal.set("test string");
logger.info(threadLocal.get());
}
可以看到我创建的threadLocal hashCode为'-2027808484'
继续执行
此时main线程中threadLocals对象已经不再是null了,这是为什么呢?
因为main线程启动后做了其他的一些初始化操作,后续可以探究。
执行完set操作后,可以看到table的size已经变成了3
hashCode为'-2027808484'的threadLocal作为key也被维护到了threadLocals的table中,value就是我们要存储的字符串对象“test string”。
在通过get获取值的时候,通过当前threadLocal的hashCode作为key值从threadLocals取出对象,再通过getValue()拿到我们存储的字符串对象。
每个线程都有自己的threadLocals对象
在Thread类中,只创建threadLocals对象,真正的维护是在ThreadLocal类中
public class Thread implements Runnable {
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
}