ThreadLocal 线程本地变量
ThreadLocal 示例
线程本地变量。
当创建一个 ThreadLocal 变量后,访问这个变量的每一个线程都拥有这个变量的一个本地副本。
示例
public class ThreadLocalDemo {
static ThreadLocal<String> localVar = new ThreadLocal<>();
public static void main(String[] args) {
Thread thread1 = new Thread(() -> {
localVar.set("Thread 1 local var");
print("thread1");
localVar.remove();
System.out.println("thread1 after remove: " + localVar.get());
});
Thread thread2 = new Thread(() -> {
localVar.set("Thread 2 local var");
print("thread2");
System.out.println("thread2 after remove: " + localVar.get());
});
thread1.start();
thread2.start();
}
static void print(String str) {
System.out.println(str + ":" + localVar.get());
}
}
thread2:Thread 2 local var
thread1:Thread 1 local var
thread2 after remove: Thread 2 local var
thread1 after remove: null
也就是 thread1 的 localVar.remove() 并不会清除 thread2 的 localVar。验证了是线程本地的变量,互相独立。
ThreadLocal 源码解读
set()
先从 localVar.set() 方法入手,进入 java.lang.ThreadLocal#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 t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
set 方法里面:
- 获取当前线程
- 将当前线程作为 key 来获取 ThreadLocalMap 类型的一个 map
- map 为 null 就新建,map 存在就直接 set 值
java.lang.ThreadLocal#getMap
/**
* 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;
}
getMap 方法本质上是获取当前线程的 threadLocals 变量,继续研究该变量。
java.lang.Thread#threadLocals
/* ThreadLocal values pertaining to this thread. This map is maintained
* by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;
回到前面的 createMap(t, value)
方法: java.lang.ThreadLocal#createMap
/**
* 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
*/
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
本质上就是创建当前线程的 threadLocals 变量。
get()
java.lang.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
*/
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();
}
获取当前线程,获取 ThreadLocalMap 实例变量 map
- map 非空,根据此线程局部变量的当前线程副本获取 entry,返回 entry 的值,该值是泛型。
- map 为空,初始化。
注意
map.getEntry(this)
中的 this 表示的是一个 ThreadLocal 对象。
java.lang.ThreadLocal#setInitialValue
/**
* 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;
}
set 方法的变体,非常类似,不同点在于提供了 value 的初始值,为 null。
remove()
public void remove() {
ThreadLocalMap m = getMap(Thread.currentThread());
if (m != null)
m.remove(this);
}
总结
每个线程内部都有一个 threadLocals 的变量,变量类型为 ThreadLocalMap,本质是 HashMap。
key 为 ThreadLocal 变量的 this 引用,value 为设置进去的值。
InheritableThreadLocal 源码解读
让子线程可以访问父线程中设置的本地变量。
public class InheritableThreadLocal<T> extends ThreadLocal<T> {
protected T childValue(T parentValue) {
return parentValue;
}
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
}
InheritableThreadLocal 继承自 ThreadLocal,重写了三个方法。其中 createMap 和 getMap 方法保证了使用 inheritableThreadLocals 代替 threadLocals。
对于该构造方法 childValue(T parentValue)
,需要先查看 Thread 的默认构造函数
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
...
Thread parent = currentThread();
...
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 */
tid = nextThreadID();
}
在创建线程时,如果 inheritThreadLocals 不为 null,那么这句:
this.inheritableThreadLocals =
ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
猜测是将父线程的 inheritableThreadLocals 的信息传递给了子线程 this.inheritableThreadLocals
。
再看 java.lang.ThreadLocal#createInheritedMap
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
createInheritedMap()内部:使用父线程的 inheritableThreadLocals,直接构造一个 ThreadLocalMap 赋值给子线程的 inheritableThreadLocals。
继续研究 ThreadLocalMap() 构造函数内部细节
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++;
}
}
}
}
关注这句: Object value = key.childValue(e.value);
,将父线程的 inheritableThreadLocals 复制到新的 ThreadLocalMap 对象中。
示例+总结
InheritableThreadLocal<String> threadLocal = new InheritableThreadLocal<>();
threadLocal.set("main thread local value");
Thread thread = new Thread(() -> System.out.println("threadLocal.get():" + threadLocal.get()));
thread.start();
System.out.println("main: "+threadLocal.get());
main: main thread local value
threadLocal.get():main thread local value
- 在主线程 new 一个 InheritableThreadLocal 对象并设置值后,父线程的 inheritableThreadLocals 变量不再为 null。
- 在主线程中创建子线程时,构造函数会把父线程的 inheritableThreadLocals 变量复制一份保存到子线程的 inheritableThreadLocals 里面。
- 于是,子线程可以获取到父线程设置的 inheritableThreadLocals 信息。
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
下一篇: 谈谈自己对于 AOP 的了解
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论