简单理解ThreadLocal: 可以操作对线程自身的ThreadLocal.ThreadLocalMap,存放属于每个线程自己的内容,线程之间相互隔离。
本质: 每个Thread对象都有一个属性ThreadLocalMap,key为线程对象本身,它由ThreadLocal维护。Thread线程可拥有多个ThreadLocal维护多个自己的线程独享的值。
场景: 用于在多线程中为每个线程提供一个独立的变量副本,可避免线程安全问题,也用作需要一个线程内上下文传递的对象,如用作当前用户信息存储。
ThreadLocal部分源码:
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//获取当前线程的Map(每个Thread都有一个ThreadLocal.ThreadLocalMap)
ThreadLocalMap map = getMap(t);
if (map != null) {
//map的key为一个ThreadLocal对象,value是存的值
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void set(T value) {
//获取当前线程
Thread t = Thread.currentThread();
//获取线程的map
ThreadLocalMap map = getMap(t);
if (map != null)
//map的key为一个ThreadLocal对象
map.set(this, value);
else
createMap(t, value);
}
注意:
- 使用ThreadLocal要特别注意保证每次使用完释放掉,不然会有内存泄漏的风险。
- 线程池干扰,我们知道Java Web中每个请求都是一个独立的线程,但是为了提高性能,Web容器如Tomcat一般都会配置线程池提高效率,核心线程是共享的,若ThreadLocal没有正确的释放以便垃圾回收,那么不同请求使用了同一个常驻线程就会导致数据混乱。
- 线程继承问题:ThreadLocal是不支持继承的,子线程无法获取到父线程的ThreadLocal,要想子线程访问父线程的ThreadLocal可以用InheritableThreadLocal InheritableThreadLocal继承自ThreadLocal。那么是如何做到的呢?看Thread的构造函数
private Thread(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
//... 省略部分代码
this.group = g;
this.daemon = parent.isDaemon();
this.priority = parent.getPriority();
if (security == null || isCCLOverridden(parent.getClass()))
this.contextClassLoader = parent.getContextClassLoader();
else
this.contextClassLoader = parent.contextClassLoader;
this.inheritedAccessControlContext =
acc != null ? acc : AccessController.getContext();
this.target = target;
setPriority(priority);
//子线程inheritableThreadLocals赋值
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
/* Stash the specified stack size in case the VM cares */
this.stackSize = stackSize;
/* Set thread ID */
this.tid = nextThreadID();
}
InheritableThreadLocal部分源码:
//JDK 11.0.9
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
/**
* Computes the child's initial value for this inheritable thread-local
* variable as a function of the parent's value at the time the child
* thread is created. This method is called from within the parent
* thread before the child is started.
* <p>
* This method merely returns its input argument, and should be overridden
* if a different behavior is desired.
*
* @param parentValue the parent thread's value
* @return the child thread's initial value
*/
protected T childValue(T parentValue) {
return parentValue;
}
/**
* Get the map associated with a ThreadLocal.
*
* @param t the current thread
*/
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
/**
* Create the map associated with a ThreadLocal.
*
* @param t the current thread
* @param firstValue value for the initial entry of the table.
*/
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}