ThreadLocal 简介
ThreadLocal 使用
ThreadLocal 原理
InheritableThreadLocal
ThreadLocal 简介
ThreadLocal 是线程本地变量,如果创建了一个ThreadLocal变量,那么访问这个变量的每个线程都会有这个变量的一个副本。
ThreadLocal 使用
private static ThreadLocal<String> mThreadLocal = new ThreadLocal<>();
private static void print(String str){
//打印当前线程本地内存中threadlocal变量的值
System.out.println(str+":"+mThreadLocal.get());
// 清除当先线程本地内存中ThreadLoccal 变量的值
mThreadLocal.remove();
}
public static void main(String[] args) {
Thread threadOne = new Thread(new Runnable() {
@Override
public void run() {
mThreadLocal.set("I am One");
print("threadOne");
System.out.println(mThreadLocal.get());
}
});
Thread threadTwo = new Thread(new Runnable() {
@Override
public void run() {
mThreadLocal.set("I am Two");
print("threadTwo");
System.out.println(mThreadLocal.get());
}
});
threadOne.start();
threadTwo.start();
}
ThreadLocal 原理
- 首先我们看一下ThreadLoca 的set 方法
// set 操作每个线程都是串行的,不会有线程安全的问题
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
// 当前 thradLocal 之前有设置值,直接设置,否则初始化
if (map != null)
map.set(this, value);
// 初始化ThreadLocalMap
else
createMap(t, value);
}
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
流程:
- 获取当前线程
- 获取当前线程中的threadLocals 对象,threadLocals 为ThreadLocalMap对象,ThreadLocalMap是ThreadLocal内部类,存储ThreadLocal 和value 的键值对
- 如果为不为空则直接将值设置到线程的threadLocals 中
- 如果为空则初始化线程的threadLocals对象,并设置值
- get 方法
public T get() {
// 因为 threadLocal 属于线程的属性,所以需要先把当前线程拿出来
Thread t = Thread.currentThread();
// 从线程中拿到 ThreadLocalMap
ThreadLocalMap map = getMap(t);
if (map != null) {
// 从 map 中拿到 entry,由于 ThreadLocalMap 在 set 时的 hash 冲突的策略不同,导致拿的时候逻辑也不太一样
ThreadLocalMap.Entry e = map.getEntry(this);
// 如果不为空,读取当前 ThreadLocal 中保存的值
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
// 否则给当前线程的 ThreadLocal 初始化,并返回初始值 null
return setInitialValue();
}
private T setInitialValue() {
// protected T initialValue() {
// return null;
// }
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
get流程:
- 获取当前线程并获取其他的threadLocals 属性
- 如果threadLocals不为空,并且获取的此ThreadLocal 对应的Entry不为空,则获取其value值
- 如果为空,线程的 ThreadLocal 初始化,并返回初始值 null
ThreadLocal不支持集成性,即在子线程中获取不到父线程中设置的value,下面我们来介绍下可继承的InheritableThreadLocal
InheritableThreadLocal
InheritableThreadLocal 是ThreadLocal 的子类,其重写了ThreadLocal 的三个方法
protected T childValue(T parentValue) {
return parentValue;
}
// 获取的是thread 的 inheritableThreadLocals 属性
ThreadLocalMap getMap(Thread t) {
return t.inheritableThreadLocals;
}
// 创建当前线程的inheritableThreadLocals 对象
void createMap(Thread t, T firstValue) {
t.inheritableThreadLocals = new ThreadLocalMap(this, firstValue);
}
以上getMap 和 createMap保证创建和获取的是Thread 的inheritableThreadLocals 属性,接下来我们看下childValue 在哪使用
在创建Thread 初始化时代码如下:
// g 代表线程组,线程组可以对组内的线程进行批量的操作,比如批量的打断 interrupt
// target 是我们要运行的对象
// stackSize 可以设置堆栈的大小
private void init(ThreadGroup g, Runnable target, String name,
long stackSize, AccessControlContext acc) {
......
// 当前线程作为父线程
Thread parent = currentThread();
.......
// 当父线程的 inheritableThreadLocals 的值不为空时
// 会把 inheritableThreadLocals 里面的值全部传递给子线程
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 */
// 线程 id 自增
tid = nextThreadID();
}
// 父线程创建子线程时,就是通过 createInheritedMap 方法传递父线程中的 inheritableThreadLocals
static ThreadLocalMap createInheritedMap(ThreadLocalMap parentMap) {
return new ThreadLocalMap(parentMap);
}
private ThreadLocalMap(ThreadLocalMap parentMap) {
// 得到父线程的 table
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")
// 拿到 key
ThreadLocal<Object> key = (ThreadLocal<Object>) e.get();
if (key != null) {
// 拿到 value, childValue 方法子类是可以重新实现的
// InheritableThreadLocal 是 ThreadLocal 的子类,InheritableThreadLocal 的 childValue 方法是直接返回 e.value,这是一种浅拷贝。
// 如果你想做一些值的拷贝的话,可以重新实现 ThreadLocal.childValue 方法,做值的深度拷贝
Object value = key.childValue(e.value);
Entry c = new Entry(key, value);
// 计算 key 在 table 的索引位置,实际上就是取模运算
int h = key.threadLocalHashCode & (len - 1);
// 如果 h 索引位置中的值已经存在了,找 i 的下一个索引位置,直到找到为空的位置为止
// 这里的冲突是很有可能发生的,比如 len 是 3,那么 6、9 对 3 取模,算出来的索引位置都是 0,于是往下找到为空的位置,这是一种简单的冲突策略吧
while (table[h] != null)
h = nextIndex(h, len);
// 赋值
table[h] = c;
size++;
}
}
}
}
以上便实现了将父线程中的值拷贝到子线程
网友评论