【Java】通俗易懂的JUC源码剖析-ThreadLocal/InheritableThreadLocal
通俗易懂的JUC源码剖析-ThreadLocal/InheritableThreadLocal
小强大人发布于 30 分钟前
一、前言
多个线程使用共享变量时,如果要保证线程安全,通常会加锁,synchronized或者Lock。但这两种锁都是重量级的,如果多个线程都希望操作各自的变量,彼此间互不影响,那么ThreadLocal就派上用场了,InheritableThreadLocal则是它的功能扩展,后面会分析它的使用场景。
二、ThreadLocal实现原理
先来看下它的类结构:
红色框中的是我们常用的方法,它内部是用ThreadLocalMap实现的,虽然命名有Map后缀,但并没有实现Map接口,来看下它的结构:
static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private Entry[] table;
}
可以看到,ThreadLocalMap内部是用Entry[]来保存线程变量的,key是ThreadLocal实例本身,并不是当前线程Thread哦,value就是要用的变量。
先来看set()方法:
public void set(T value) {Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else createMap(t, value);
}
①先获取当前线程t对应的ThreadLocalMap实例,getMap代码如下:
ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}
可以看到,Thread内部有个ThreadLocalMap类型的引用
Thread.java
ThreadLocal.ThreadLocalMap threadLocals = null;
②如果map不为空,保存值,this即为当前ThreadLocal实例,value为我们要用的变量
③如果map为空,则createMap,代码如下:
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}
来看看ThreadLocalMap的构造函数做了哪些事情:
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {// 初始化Entry[]
table = new Entry[INITIAL_CAPACITY];
// 计算firstValue放在该数组的位置i
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
// 赋值
table[i] = new Entry(firstKey, firstValue);
size = 1;
// 设置阈值,扩容会用到
setThreshold(INITIAL_CAPACITY);
}
setThreshold方法如下:
/*** Set the resize threshold to maintain at worst a 2/3 load factor. */
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
代码也很简单,看注释就能明白,这里的实现思路和HashMap差不多。
再来看get()方法:
public T get() {// 获取当前线程t
Thread t = Thread.currentThread();
// 获取当前线程t对应的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 获取当前ThreadLocal实例对应的线程变量
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// map为空则初始化
return setInitialValue();
}
setInitialValue()代码如下:
private T setInitialValue() {// 获取设定的初始化值,默认为null,用户可以调用它来设置初始值
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// map不为空则设置初始化值,否则创建map
if (map != null)
map.set(this, value);
else createMap(t, value);
return value;
}
最后看下remove()方法:
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());
// 删除当前ThreadLocal实例对应的变量
if (m != null)
m.remove(this);
}
常用的set(),get(),remove()方法逻辑都很清晰明了,就不做赘述了
三、InheritableThreadLocal的作用
我们知道,ThreadLocal在使用过程中,各个线程之间的变量是互不影响的,子线程没法拿到父线程的本地变量,这也是正常的。但有时候也有这样的需求场景,子线程需要拿到父线程的变量,比如子线程需要使用存放在ThreadLocal变量中的用户登录信息,再比如一些中间件需要把统一的id追踪的整个调用链路记录下来。那么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,并重写了getMap()和createMap(),里面出现了个t.inheritableThreadLocals,它就是实现共享变量的关键,它替换了t.threadLocals,所有原先对t.threadLocals的操作都改成了t.inheritableThreadLocals。它和t.threadLocals类型一样,也是Thread的一个属性。
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
但目前好像还是看不出来起共享效果的是哪段代码?它隐藏的很深,在Thread的构造函数里,来看看吧:
public Thread(Runnable target) {init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
...
// parent指的是调用new Thread()的线程,即main线程
Thread parent = currentThread();
...
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
...
}
为了方便讲解,省略了些代码,只看对inheritableThreadLocals起作用的。
来看看ThreadLocal.createInheritedMap()代码:
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {return new ThreadLocalMap(parentMap);
}
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) {
// childValue()就是父线程的e.value
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++;
}
}
}
}
可以看到,构造函数就是把父线程的ThreadLocalMap里的Entry[]拷贝到子线程中,实现变量的共享。
到这里本文就结束了,谢谢观看。
阅读 21更新于 26 分钟前
本作品系原创,采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议
小强大人
4 声望
0 粉丝
小强大人
4 声望
0 粉丝
宣传栏
一、前言
多个线程使用共享变量时,如果要保证线程安全,通常会加锁,synchronized或者Lock。但这两种锁都是重量级的,如果多个线程都希望操作各自的变量,彼此间互不影响,那么ThreadLocal就派上用场了,InheritableThreadLocal则是它的功能扩展,后面会分析它的使用场景。
二、ThreadLocal实现原理
先来看下它的类结构:
红色框中的是我们常用的方法,它内部是用ThreadLocalMap实现的,虽然命名有Map后缀,但并没有实现Map接口,来看下它的结构:
static class ThreadLocalMap {static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
private Entry[] table;
}
可以看到,ThreadLocalMap内部是用Entry[]来保存线程变量的,key是ThreadLocal实例本身,并不是当前线程Thread哦,value就是要用的变量。
先来看set()方法:
public void set(T value) {Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else createMap(t, value);
}
①先获取当前线程t对应的ThreadLocalMap实例,getMap代码如下:
ThreadLocalMap getMap(Thread t) {return t.threadLocals;
}
可以看到,Thread内部有个ThreadLocalMap类型的引用
Thread.java
ThreadLocal.ThreadLocalMap threadLocals = null;
②如果map不为空,保存值,this即为当前ThreadLocal实例,value为我们要用的变量
③如果map为空,则createMap,代码如下:
void createMap(Thread t, T firstValue) {t.threadLocals = new ThreadLocalMap(this, firstValue);
}
来看看ThreadLocalMap的构造函数做了哪些事情:
ThreadLocalMap(ThreadLocal<?> firstKey, Object firstValue) {// 初始化Entry[]
table = new Entry[INITIAL_CAPACITY];
// 计算firstValue放在该数组的位置i
int i = firstKey.threadLocalHashCode & (INITIAL_CAPACITY - 1);
// 赋值
table[i] = new Entry(firstKey, firstValue);
size = 1;
// 设置阈值,扩容会用到
setThreshold(INITIAL_CAPACITY);
}
setThreshold方法如下:
/*** Set the resize threshold to maintain at worst a 2/3 load factor. */
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
代码也很简单,看注释就能明白,这里的实现思路和HashMap差不多。
再来看get()方法:
public T get() {// 获取当前线程t
Thread t = Thread.currentThread();
// 获取当前线程t对应的ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 获取当前ThreadLocal实例对应的线程变量
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// map为空则初始化
return setInitialValue();
}
setInitialValue()代码如下:
private T setInitialValue() {// 获取设定的初始化值,默认为null,用户可以调用它来设置初始值
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// map不为空则设置初始化值,否则创建map
if (map != null)
map.set(this, value);
else createMap(t, value);
return value;
}
最后看下remove()方法:
public void remove() {ThreadLocalMap m = getMap(Thread.currentThread());
// 删除当前ThreadLocal实例对应的变量
if (m != null)
m.remove(this);
}
常用的set(),get(),remove()方法逻辑都很清晰明了,就不做赘述了
三、InheritableThreadLocal的作用
我们知道,ThreadLocal在使用过程中,各个线程之间的变量是互不影响的,子线程没法拿到父线程的本地变量,这也是正常的。但有时候也有这样的需求场景,子线程需要拿到父线程的变量,比如子线程需要使用存放在ThreadLocal变量中的用户登录信息,再比如一些中间件需要把统一的id追踪的整个调用链路记录下来。那么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,并重写了getMap()和createMap(),里面出现了个t.inheritableThreadLocals,它就是实现共享变量的关键,它替换了t.threadLocals,所有原先对t.threadLocals的操作都改成了t.inheritableThreadLocals。它和t.threadLocals类型一样,也是Thread的一个属性。
ThreadLocal.ThreadLocalMap inheritableThreadLocals = null;
但目前好像还是看不出来起共享效果的是哪段代码?它隐藏的很深,在Thread的构造函数里,来看看吧:
public Thread(Runnable target) {init(null, target, "Thread-" + nextThreadNum(), 0);
}
private void init(ThreadGroup g, Runnable target, String name,long stackSize) {
init(g, target, name, stackSize, null, true);
}
private void init(ThreadGroup g, Runnable target, String name,long stackSize, AccessControlContext acc,
boolean inheritThreadLocals) {
...
// parent指的是调用new Thread()的线程,即main线程
Thread parent = currentThread();
...
if (inheritThreadLocals && parent.inheritableThreadLocals != null)
this.inheritableThreadLocals = ThreadLocal.createInheritedMap(parent.inheritableThreadLocals);
...
}
为了方便讲解,省略了些代码,只看对inheritableThreadLocals起作用的。
来看看ThreadLocal.createInheritedMap()代码:
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {return new ThreadLocalMap(parentMap);
}
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) {
// childValue()就是父线程的e.value
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++;
}
}
}
}
可以看到,构造函数就是把父线程的ThreadLocalMap里的Entry[]拷贝到子线程中,实现变量的共享。
到这里本文就结束了,谢谢观看。
以上是 【Java】通俗易懂的JUC源码剖析-ThreadLocal/InheritableThreadLocal 的全部内容, 来源链接: utcz.com/a/107210.html
得票时间