深入了解ThreadLocal的话,可以看下 深入细节ThreadLocalMap
一、ThreadLocal是什么
ThreadLocal是一个工具,可以操作当前线程的ThreadLocalMap数据,ThreadLocalMap数据不能跨线程,为解决多线程程序的并发问题提供了一种新的思路。
二、ThreadLocal使用
有时我们不想当前线程中的值被其他线程修改。下面示例可能不能准确地表达思想,误喷啊。。
代码语言:javascript复制public int TestValue=0;
public static void main(String[] args) {
Test test=new Test();
System.out.println("当前线程 值" test.TestValue);
test.updateValue();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程 值" test.TestValue);
}
代码语言:javascript复制public void updateValue(){
Thread T1=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("新线程 修改前 值" TestValue);
TestValue=1;
System.out.println("新线程 修改后 值:" TestValue);
}
});
T1.start();
}
我们可以得到以下输出
我们可以看到新线程修改TestValue值后,当前线程受影响了。
如果我们用ThreadLocal变量呢,这样的行为会不会影响我们程序使用?我们来看下
代码语言:javascript复制public ThreadLocal<Integer> TestLocalValue2=new ThreadLocal<>();
public static void main(String[] args) {
Test test=new Test();
test.TestLocalValue2.set(0);
System.out.println("当前线程 值" test.TestLocalValue2.get());
test.updateThreadLocalValue();
try {
Thread.sleep(2000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("当前线程 值" test.TestLocalValue2.get());
}
public void updateThreadLocalValue(){
Thread T1=new Thread(new Runnable() {
@Override
public void run() {
System.out.println("新线程 修改前 值" TestLocalValue2.get());
TestLocalValue2.set(1);
System.out.println("新线程 修改后 值" TestLocalValue2.get());
}
});
T1.start();
}
我们可以得到以下输出
我们可以发现,
1.新线程修改ThreadLocal变量数据,对当前线程不受影响
2.新线程拿不到当前线程ThreadLocal变量数据,新线程修改ThreadLocal变量前,拿到的数据是null。
三、源码粗略说明
我们来看下get方法里面做了什么事
代码语言:javascript复制public T get() {
Thread t = Thread.currentThread(); //获取当前线程
ThreadLocalMap map = getMap(t);//获取该线程中ThreadLocalMap数据
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);//获取当前线程ThreadLocal为key的Entry数据
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();//创建ThreadLocalMap同时返回null
}
代码语言:javascript复制ThreadLocalMap getMap(Thread t) { //t 是当前线程
return t.threadLocals; //默认初始值是null
}
代码语言:javascript复制private T setInitialValue() {
T value = initialValue(); // 默认初始值null
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value); //创建 ThreadLocalMap
return value; //返回null
}
代码语言:javascript复制void createMap(Thread t, T firstValue) {
t.threadLocals = new ThreadLocalMap(this, firstValue);
}
代码语言:javascript复制private Entry getEntry(ThreadLocal<?> key) {
int i = key.threadLocalHashCode & (table.length - 1);
Entry e = table[i];
if (e != null && e.get() == key)
return e;
else
return getEntryAfterMiss(key, i, e);
}
get方法大致流程是
1.获取当前线程,找到当前线程ThreadLocalMap数据
2.如果ThreadLocalMap不为null,且有数据,则返回数据。
3.如果ThreadLocalMap不为null,但是没有数据,则把null添加到ThreadLocalMap中,同时返回null。
4.如果ThreadLocalMap为null,则创建ThreadLocalMap,同时返回null。
我们再来看下set方法
代码语言:javascript复制public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
大致流程是
1.获取当前线程,找到当前线程ThreadLocalMap数据。
2.如果ThreadLocalMap不为null,则把数据添加到ThreadLocalMap中。
3.如果ThreadLocalMap为null,则创建ThreadLocalMap。
我们可以看到ThreadLocal里面核心是ThreadLocalMap。ThreadLocal数据的操作底层是由ThreadLocalMap完成的。我们来看看ThreadLocalMap结构
代码语言:javascript复制
private static final int INITIAL_CAPACITY = 16;// 初始容量 —— 必须是2的冥
private Entry[] table;//存放数据的table
private int size = 0;// 数组里面entrys的个数,可以用于判断table当前使用量是否超过负因子
private int threshold; // Default to 0 进行扩容的阈值,表使用量大于它的时候进行扩容
Entry存储结构
代码语言:javascript复制static class Entry extends WeakReference<ThreadLocal<?>> {
/** The value associated with this ThreadLocal. */
Object value;
Entry(ThreadLocal<?> k, Object v) {
super(k);
value = v;
}
}
Entry继承WeakReference,使用弱引用,可以将ThreadLocal对象的生命周期和线程生命周期解绑,持有对ThreadLocal的弱引用,可以使得ThreadLocal在没有其他强引用的时候被回收掉,这样可以避免因为线程得不到销毁,导致ThreadLocal对象无法被回收的问题。
四、简单总结
整个流程不复杂。里面主要涉及到三个主体,Thread、TheadLocalMap、TheadLocal。我们画个关系图来看下
TheadLocal中的get、set方法实际是调用当前线程Thread中TheadLocalMap的get、set方法,对Entry数组进行各种操作。同时TheadLocal是Entry中的key。
以上是对TheadLocal简单了解,对TheadLocal深入了解,可以看下 深入细节ThreadLocalMap