目录
一 ThreadLocal 是什么
ThreadLocal 本地线程变量,通过 set() 和 get() 方法来维护局部变量。不同线程之间局部变量彼此隔离,互不影响。
二 ThreadLocal 实现原理
2.1 我们可以先来看看 ThreadLocal、Thread、ThreadLocalMap 之间的关系。
2.1.1 ThreadLocalMap 类属于 ThreadLocal 类的静态内部类
public class ThreadLocal<T> {
/**
* ThreadLocals rely on per-thread linear-probe hash maps attached
* to each thread (Thread.threadLocals and
* inheritableThreadLocals). The ThreadLocal objects act as keys,
* searched via threadLocalHashCode. This is a custom hash code
* (useful only within ThreadLocalMaps) that eliminates collisions
* in the common case where consecutively constructed ThreadLocals
* are used by the same threads, while remaining well-behaved in
* less common cases.
*/
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;
}
}
}
}
2.1.2 Thread 类持有 ThreadLocal.ThreadLocalMap 变量。
public
class Thread implements Runnable {
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
2.2 set() 和 get() 方法
2.2.1 set() 方法
/**
* 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
* 通过当前 Thread 获取 ThreadLocalMap
* 如果 ThreadLocalMap 不为 null,往 ThreadLocalMap 塞值,key 为当前 ThreadLocal
* 如果 ThreadLocalMap 为 null 创建 ThreadLocalMap
*/
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
/**
* Get the map associated with a ThreadLocal. Overridden in
* InheritableThreadLocal.
*
* @param t the current thread
* @return the map
*/
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
当前 Thread 持有 ThreadLocalMap 变量。
2.2.2 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
*/
public T get() {
/**
* 获取当前 Thread
* 通过当前 Thread 获取 ThreadLocalMap
* 如果 ThreadLocalMap 不为 null,通过当前 ThreadLocal 获取 Entry,之后获取值
* 如果 ThreadLocalMap 为 null,初始化 ThreadLocalMap
*/
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();
}
综上所述:
- 每个 Thread 维护着一个 ThreadLocalMap 的引用。
- ThreadLocalMap 是 ThreadLocal 的内部类,用 Entry 来进行存储。
- 调用 ThreadLocal 的 set() 方法时,实际上就是往 ThreadLocalMap 设置值,key 是ThreadLocal 对象,值是传递进来的对象。
- 调用 ThreadLocal 的 get() 方法时,实际上就是往 ThreadLocalMap 获取值,key是 ThreadLocal 对象。
- ThreadLocal 本身并不存储值,它只是作为一个 key 来让 Thread 从 ThreadLocalMap 获取 value。
三 ThreadLocal 应用场景
- 每个线程需要有自己单独的实例。
- 实例需要在当前线程的多个方法中共享。
在实际应用中是,一个 Thread 持有一个 ThreadLocalMap 变量,一个 ThreadLocalMap 可以持有多个 ThreadLocal 对象。由于 ThreadLocalMap 和 Thread 的生命周期一样长,所以,当往 ThreadLocalMap 中设置 ThreadLocal 对象,使用完之后一定要调用 remove()方法,防止内存泄漏。
/**
* Removes the current thread's value for this thread-local
* variable. If this thread-local variable is subsequently
* {@linkplain #get read} by the current thread, its value will be
* reinitialized by invoking its {@link #initialValue} method,
* unless its value is {@linkplain #set set} by the current thread
* in the interim. This may result in multiple invocations of the
* {@code initialValue} method in the current thread.
*
* @since 1.5
*/
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
四 InheritableThreadLocal 是什么
为了实现父子线程共享本地变量。
五 InheritableThreadLocal 实现原理
代码演示:
public class InheritableThreadLocalDemo {
private static ThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
public static void main(String[] args) {
// 主线程
threadLocal.set("hello world");
System.out.println("main: " + threadLocal.get());
// 启动子线程
Thread thread = new Thread(() -> {
// 子线程输出父线程的threadLocal 变量值
System.out.println("子线程: " + threadLocal.get());});
thread.start();
threadLocal.remove();
}
}
运行结果:
从结果上看,子线程确实获取到了主线程的线程变量。
InheritableThreadLocal 源码:
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
public InheritableThreadLocal() {
}
protected T childValue(T var1) {
return var1;
}
ThreadLocalMap getMap(Thread var1) {
return var1.inheritableThreadLocals;
}
void createMap(Thread var1, T var2) {
var1.inheritableThreadLocals = new ThreadLocalMap(this, var2);
}
}
从源码看 InheritableThreadLocal 继承了 ThreadLocal 之后的 get、set 操作的都是 Thread 的
inheritableThreadLocals 。
public class Thread implements Runnable {
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
/*
* InheritableThreadLocal values pertaining to this thread. This map is
* maintained by the InheritableThreadLocal class.
*/
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
}
父子线程是如何共享线程变量的呢
public class Thread implements Runnable {
/**
* Allocates a new {@code Thread} object. This constructor has the same
* effect as {@linkplain #Thread(ThreadGroup,Runnable,String) Thread}
* {@code (null, null, gname)}, where {@code gname} is a newly generated
* name. Automatically generated names are of the form
* {@code "Thread-"+}<i>n</i>, where <i>n</i> is an integer.
*/
public Thread() {
init(null, null, "Thread-" + nextThreadNum(), 0);
}
/**
* Initializes a Thread.
*
* @param g the Thread group
* @param target the object whose run() method gets called
* @param name the name of the new Thread
* @param stackSize the desired stack size for the new thread, or
* zero to indicate that this parameter is to be ignored.
* @param acc the AccessControlContext to inherit, or
* AccessController.getContext() if null
*/
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
if (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 */
tid = nextThreadID();
}
}
在子线程构造器的时候将父线程的线程变量复制一份到了子线程。
/**
* Construct a new map including all Inheritable ThreadLocals
* from given parent map. Called only by createInheritedMap.
*
* @param parentMap the map associated with parent thread.
*/
private ThreadLocalMap(ThreadLocalMap parentMap) {
Entry[] parentTable = parentMap.table;
int len = parentTable.length;
setThreshold(len);
table = new Entry[len];
for (int j = 0; j < len; j++) {
Entry e = parentTable[j];
if (e != null) {
@SuppressWarnings("unchecked")
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
int h = key.threadLocalHashCode & (len - 1);
while (table[h] != null)
h = nextIndex(h, len);
table[h] = c;
size++;
}
}
}
}