ThreadLocal
是一种解决多线程环境下成员变量问题的一个解决方案,与线程同步无关。
1. 设计思路:
为每个线程创建一个属于线程自己的变量副本,从而每个线程都可以独立的改变自己所拥有的副本,而不会影响其他线程所对应的副本。
白话:ThreadLocal说白了,就是为每个线程自己提供了一个新的副本对象,各自线程拥有各自的,这样就不会对别人有任何妨碍了。
2. 为啥需要引用ThreadLocal ?
不是为了解决共享变量问题,也不是为了线程同步而存在,而是为了便于每个线程能够处理自己的状态而引入的一种机制。
3. 主要方法
3.1 get()
返回该线程局部变量的当前线程的副本的值
3.2 initialValue()
返回该线程局部变量副本的初始值
3.3 remove()
移除当前线程局部变量副本值
3.4 set(T v)
将此线程局部变量的当前副本中的值设置为指定值
4. ThreadLocalMap
实现该机制的关键内部类,属于ThreadLocal中的内部类
ThreadLocalMap 是一种定制的哈希映射,仅适用于维护线程本地值。 不会在 ThreadLocal 类之外导出任何操作。 该类是包私有的,以允许在类 Thread 中声明字段。 为了帮助处理非常大且长期存在的用法,哈希表条目使用.
在Thread类中位置
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
- 在Thread类中维护了这样ThreadLocal.ThreadLocalMap一个类型的成员变量,该成员变量用来存储实际的ThreadLocal变量副本。
- 提供了一种用键值对方式存储每个线程变量副本的方法,key为当前ThreadLocal对象,value则是对呀线程的变量副本
5. 注意点
- ThreadLocal本身不存储键值,只是提供了一个在当前线程中找到副本的key,ThreadLocal对象引用地址就是key
- 是ThreadLocal包含在Thread中,不是Thread包含在ThreadLocal中
6. 内存泄露问题
-
ThreadLocalMap: key弱引用,value强引用,无法回收
-
显示调用remove导致
2. 关键源码赏析
2.1 ThreadLocal
前置
//创建一个对象,并赋初始值
ThreadLocal<Integer> threadLocal = new ThreadLocal<Integer>(){
@Override
protected Integer initialValue() {
return 0;
}
};
//获取副本值
threadLocal.get();
源码
/**
* 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
*/
//获取变量副本值,threadLocal
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();
}
/**
* Create the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the map
*/
//threadLocalMap,this就是ThreadLocal对象引用地址,这里作为key
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
/**
* 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);
}
/**
* Variant of set() to establish initialValue. Used instead
* of set() in case user has overridden the set() method.
*
* @return the initial value
*/
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}