ThreadLocal是什么
我们知道在使用多线程时,有时需要共享变量,需要同步数据,所以各个线程之间对这个变量都是开放的,这概念可以称之为线程开放。但也不是所有时候都需要同步数据,每个线程也可以有自己独有的变量,不需要同步给其他线程,这概念可以叫做线程封闭。而ThreadLocal就是线程封闭具体的体现之一。在JDK1.2版本里就提供了java.lang.ThreadLocal类,我们可以把ThreadLocal理解为线程的局部变量,是一个线程级别的变量。
ThreadLocal的简单实践
demo代码如下:
public class TestMain {
public static void main(String[] args) throws Exception {
ThreadLocal<String> value = new ThreadLocal<>();
value.set("主线程");
String threadLocal = value.get();
System.out.println("执行线程一前主线程threadLocal值为:" + threadLocal);
Thread thread = new Thread(new Runnable() {
@Override
public void run() {
String threadLocal = value.get();
System.out.println("设置线程一threadLocal值前,值为:" + threadLocal);
value.set("线程一");
String threadLocal1 = value.get();
System.out.println("设置线程一threadLocal值后,值为:" + threadLocal1);
}
});
thread.start();
thread.join();
System.out.println("线程一运行完后,主线程threadLocal值为" + threadLocal);
}
}
运行结果如下:
可以看到主线程的ThreadLocal的值一直是"主线程",线程一在没设置值前是null,设置后为"线程一",两个线程的ThreadLocal值互不影响,各自为政。
ThreadLocal的get()方法的源码分析
get()方法源码:
public T get() {
//获取当前线程
Thread t = Thread.currentThread();
//以线程为参数获取ThreadLocalMap对象
ThreadLocalMap map = getMap(t);
if (map != null) {
//从map里拿到线程对应的ThreadLocalMap.Entry对象
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
//获取ThreadLocalMap.Entry对象里的value值
T result = (T)e.value;
//返回结果
return result;
}
}
//如果该线程的ThreadLocal值为空,则设置初始值返回
return setInitialValue();
}
ThreadLocalMap类源码:
static class ThreadLocalMap {
//静态内部类Entry
static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
/**
* The initial capacity -- MUST be a power of two.
*/
private static final int INITIAL_CAPACITY = 16;
//Entry数组
private Entry[] table;
/**
* The number of entries in the table.
*/
private int size = 0;
/**
* The next size value at which to resize.
*/
private int threshold; // Default to 0
/**
* Set the resize threshold to maintain at worst a 2/3 load factor.
*/
private void setThreshold(int len) {
threshold = len * 2 / 3;
}
/**
* Increment i modulo len.
*/
private static int nextIndex(int i, int len) {
return ((i + 1 < len) ? i + 1 : 0);
}
/**
* Decrement i modulo len.
*/
private static int prevIndex(int i, int len) {
return ((i - 1 >= 0) ? i - 1 : len - 1);
}
getMap()方法源码:
//threadLocals变量是ThreadLocalMap类
ThreadLocal.ThreadLocalMap threadLocals = null;
//返回ThreadLocalMap对象
ThreadLocalMap getMap(Thread t) {
return t.threadLocals;
}
getEntry()方法源码:
private Entry getEntry(ThreadLocal<?> key) {
//通过threadLocalHashCode进行一个位运算(取模)得到索引i
int i = key.threadLocalHashCode & (table.length - 1);
//通过索引值获取Entry实例
Entry e = table[i];
//满足条件下返回Entry,否则走另一个分支
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
getEntryAfterMiss()方法源码:
private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
Entry[] tab = table;
int len = tab.length;
//Entry不为空时做的逻辑
while (e != null) {
ThreadLocal<?> k = e.get();
if (k == key)
return e;
if (k == null)
expungeStaleEntry(i);
else
i = nextIndex(i, len);
e = tab[i];
}
//返回空值
return null;
}
setInitialValue()方法源码:
private T setInitialValue() {
//返回null
T value = initialValue();
//取得当前线程
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
//如果ThreadLocalMap对象不为空,则为当前ThreadLocal对象设置对应value值
map.set(this, value);
else
//创建ThreadLocalMap为当前线程存储ThreadLocal的value值
createMap(t, value);
return value;
}
//初始化值为null
protected T initialValue() {
return null;
}
set()方法源码:
private void set(ThreadLocal<?> key, Object value) {
Entry[] tab = table;
int len = tab.length;
//通过threadLocalHashCode进行一个位运算(取模)得到索引i
int i = key.threadLocalHashCode & (len-1);
//满足括号里条件的设值逻辑
for (Entry e = tab[i];
e != null;
e = tab[i = nextIndex(i, len)]) {
ThreadLocal<?> k = e.get();
//如果入参的ThreadLocal实例key和k相等,则把入参的value值设置为这个Entry的value值
if (k == key) {
e.value = value;
return;
}
//k为空逻辑
if (k == null) {
replaceStaleEntry(key, value, i);
return;
}
}
//以入参key和value为参数,创建新的Entry对象,赋值给下标为i的Entry对象
tab[i] = new Entry(key, value);
int sz = ++size;
if (!cleanSomeSlots(i, sz) && sz >= threshold)
rehash();
}
createMap源码:
void createMap(Thread t, T firstValue) {
//创建当前线程的ThreadLocalMap对象实例threadLocals,并赋值
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
从以上源码可以知道,ThreadLocal里面有个静态内部类ThreadLocalMap,ThreadLocalMap类里面维护了一个Entry数组,我们的get()方法就是先以当前线程为参数取得ThreadLocalMap对象,然后再以ThreadLocal为参数通过threadLocalHashCode进行一个位运算(取模)得到索引i,接着通过索引i从该对象的属性Entry数组里面取出对应下标的value。而如果线程没有对应的ThreadLocalMap,或者ThreadLocalMap对象没有对应的Entry对象,那么就会进入初始化方法setInitialValue()方法,这个方法里会先初始化value值为空,然后判断当前线程的ThreadLocalMap对象是否为空,不为空,则调用set()方法设值,为空则调用createMap()方法设值,而这里我们要知道的是无论是走哪套设值逻辑,设置的值都是初始化的value值null,这也就能解释为什么线程一开始调用get()方法时,得到的值是null了。
总结
殊途同归,我相信通过对get()方法的简单分析,我们也能够对ThreadLocal类里的一些变量和方法有一定的理解。所以如果数据是以线程为作用域,且要求每个线程有自己的副本时,类似于会话等,那么我们可以选择使用ThreadLocal。
来源:CSDN
作者:绅士jiejie
链接:https://blog.csdn.net/weixin_38106322/article/details/104109398