- 一:什么是threadlocal?
首先看下jdk里面的解释(来自于百度翻译)
简单的总结就是:
ThreadLocal提供了线程的本地变量,他可以保证访问到的变量属于当前线程,每个线程都保存有一个变量的副本,每一个线程的变量都不同,ThreadLocal提供了一种线程的隔离,将变量与线程绑定.
ThreadLocal通过threadLocalHashCode来标识每一个threadLocal的唯一性,threadLocalHashCode通过CAS操作进行更新,每次hash操作的增量为0× 0x61c88647
可以看下他的set方法
通过Thread.currentThread()方法获取了当前的线程引用,并传给了getMap(Thread)方法获取一个ThreadLocalMap的实例。
继续看往下看源码:
可以看到getMap(Thread)方法直接返回Thread实例的成员变量threadLocals。它的定义在Thread内部,访问级别为package级别
但后打开我们的thread类;
到了这里我们可以看出,每一个Thread类里面都有一个ThreadLocal.ThreadLocalMap的成员变量,也就是说每个线程通过ThreadLocal.ThreadLocalMap与ThreadLocal相绑定,这样可以确保每个线程访问到thread-local variable都是本线程.
继续往下走,获取了ThreadLocalMap实例以后,如果他不能为空则调用ThreadLocal的createMap方法,new一个ThreadLocalMap实例并且赋值给Thread.threadLocals
ThreadLocal 的 createMap方法的源码如下:
再往下;ThreadLocal 的 get 方法
通过Thread.currentThread()方法获取了当前的线程引用,并传给了getMap(Thread)方法获取一个ThreadLocalMap的实例。继续跟进setInitialValue()方法:
首先调用 initialValue()方法来初始化,然后 通过Thread.currentThread()方法获取了当前的线程引用,并传给了getMap(Thread)方法获取一个ThreadLocalMap的实例,并将 初始化值存到ThreadLocalMap 中。
接下来我们来看看ThreadLocalMap的实现
ThreadLocalMap是ThreadLocal的静态内部类
/**
* ThreadLocalMap is a customized hash map suitable only for
* maintaining thread local values. No operations are exported
* outside of the ThreadLocal class. The class is package private to
* allow declaration of fields in class Thread. To help deal with
* very large and long-lived usages, the hash table entries use
* WeakReferences for keys. However, since reference queues are not
* used, stale entries are guaranteed to be removed only when
* the table starts running out of space.
*/
static class ThreadLocalMap {
/**
* The entries in this hash map extend WeakReference, using
* its main ref field as the key (which is always a
* ThreadLocal object). Note that null keys (i.e. entry.get()
* == null) mean that the key is no longer referenced, so the
* entry can be expunged from table. Such entries are referred to
* as "stale entries" in the code that follows.
*/
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16;
/**
* The table, resized as necessary.
* table.length MUST always be a power of two.
*/
private Entry[] table;
/**
* The number of entries in the table.
*/
private int size = 0;
/**
* The next size value at which to resize.
*/
private int threshold; // Default to 0
/**
* Set the resize threshold to maintain at worst a 2/3 load factor.
*/
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
/**
* Increment i modulo len.
*/
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
/**
* Decrement i modulo len.
*/
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
/**
* Construct a new map initially containing (firstKey, firstValue).
* ThreadLocalMaps are constructed lazily, so we only create
* one when we have at least one entry to put in it.
*/
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {
table = new Entry[INITIAL_CAPACITY];
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
table[i] = new Entry(firstKey, firstValue);
size = 1;
setThreshold(INITIAL_CAPACITY);
}
(截图无法满足要求,直接把代码copy出来)
其中INITIAL_CAPACITY代表这个Map的初始容量;1是一个Entry类型的数组,用于存储数据;size代表表中的存储数目;threshold代表需要扩容时对应size的阈值。Entry类是ThreadLocalMap的静态内部类,用于存储数据。Entry类继承了WeakReference<ThreadLocal<?>>,即每个Entry对象都有一个ThreadLocal的弱引用(作为key),这是为了防止内存泄露。一旦线程结束,key变为一个不可达的对象,这个Entry就可以被GC了。接下来我们来看ThreadLocalMap 的set方法的实现:
ThreadLocal 的get方法会调用 ThreadLocalMap 的 getEntry(ThreadLocal key) ,其源码如下:
总结了下;
每个线下的内部都有一个名字为threadLocals的成员变量,该变量的类型为HashMap,其中的key是我们定义的ThreadLocal变量的this引用,values则为我们set时候的值,每个线程本地变量是存到线程自己的内部线程自己的内存变量threadLocals里面的,如果当前线程一直不消失那么这些本地变量会一直存到,所以可能会造成内存溢出,,所以使用完毕后要记得调用ThreadLocal的remove方法删除对应线程的threadLocals中的本地变量。
如果你有更加好的方法或者思路可以一起学习一起进步,欢迎一起学习