【Java】通俗易懂的JUC源码剖析-ThreadLocal/InheritableThreadLocal

通俗易懂的JUC源码剖析-ThreadLocal/InheritableThreadLocal

小强大人发布于 30 分钟前

一、前言

多个线程使用共享变量时,如果要保证线程安全,通常会加锁,synchronized或者Lock。但这两种锁都是重量级的,如果多个线程都希望操作各自的变量,彼此间互不影响,那么ThreadLocal就派上用场了,InheritableThreadLocal则是它的功能扩展,后面会分析它的使用场景。

二、ThreadLocal实现原理

先来看下它的类结构:
【Java】通俗易懂的JUC源码剖析-ThreadLocal/InheritableThreadLocal
红色框中的是我们常用的方法,它内部是用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

阅读 21更新于 26 分钟前

本作品系原创,采用《署名-非商业性使用-禁止演绎 4.0 国际》许可协议

avatar

小强大人

4 声望

0 粉丝

0 条评论

得票时间

avatar

小强大人

4 声望

0 粉丝

宣传栏

一、前言

多个线程使用共享变量时,如果要保证线程安全,通常会加锁,synchronized或者Lock。但这两种锁都是重量级的,如果多个线程都希望操作各自的变量,彼此间互不影响,那么ThreadLocal就派上用场了,InheritableThreadLocal则是它的功能扩展,后面会分析它的使用场景。

二、ThreadLocal实现原理

先来看下它的类结构:
【Java】通俗易懂的JUC源码剖析-ThreadLocal/InheritableThreadLocal
红色框中的是我们常用的方法,它内部是用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

回到顶部