ThreadLoal是什么
ThreadLocal是变量的一种类型,比如你在主线程定义了一个变量String a,然后在这个主线程中使用Thread起了5个(序号分别是 1 2 3 4 5)子线程,如果每个线程中都用了这个a变量,那你就需要考虑线程之间的互相影响,因为线程2改变了a的值,那么在线程1中a的值也会变了。
我们如果把a的类型换成ThreadLocal<String>,那么线程2中改变a的值,并不会改变线程1中a的值,假设这5个子线程在运行之初,a的初始值都是madan,即使线程2中把a改成了mabi,在线程1中a的值还是madan(除非线程1中自己去改变a的值)
ThreadLoal能干什么
ThreadLocal的这种特性能干点什么实际意义的活呢?
- 减少同一个线程内多个函数或者模块之间变量的传递的复杂度(说实话目前我没有体验到同一个线程之间变量传递哪里复杂了!!!能复杂到哪去?)
- 解决并发下对临界资源的访问问题,这句话如果说人话,就是下面的解释
假如我有多个线程都要用到变量cao,这里的用到是指在在该变量的值载入线程开始,我这个线程就不希望这个值被别的线程改变了(当然线程自己可以修改)
使用ThreadLocal类型定义这个cao,就会省去很多麻烦
ThreadLoal为什么能干这些内容(即源码分析)
在此必须先吐槽一下网上的教程,都是来回复制粘贴的,来龙去脉屁都没有说清楚,还有的就是上来就是贴一堆源码,不能循序渐进的去介绍,看的人一脸懵逼啊~~~
要想搞明白ThreadLocal变量,必须搞明白以下三个类的调用关系
源码看不明白没关系,可以直接看下面的例子代码,然后对照着源码看
Thread类、ThreadLocal类、ThreadLocalMap类(这个类是ThreadLocal的内部类)
- ThreadLocal的核心源码
public class ThreadLocal<T> {
public ThreadLocal() {} // 构造函数
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
public T get() {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null) {
ThreadLocalMap.Entry e = map.getEntry(this);
if (e != null) {
@SuppressWarnings("unchecked")
T result = (T)e.value;
return result;
}
}
return setInitialValue();
}
public void set(T value) {
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
}
private T setInitialValue() {
T value = initialValue();
Thread t = Thread.currentThread();
ThreadLocalMap map = getMap(t);
if (map != null)
map.set(this, value);
else
createMap(t, value);
return value;
}
static class ThreadLocalMap{} // 内部类
}
- Thread类
public class Thread implements Runnable {
ThreadLocal.ThreadLocalMap threadLocals = null;
public Thread(Runnable target) {
init(null, target, "Thread-" + nextThreadNum(), 0);
}
// 其它代码
}
源码看不懂没有关系,现在来看一下演示例子,我会一步一步去解释
public class TestThreadLocal { // 测试threadlocal的类
private static final ThreadLocal<Integer> value = new ThreadLocal<Integer>() {
//重写了ThreadLocal类中的initialValue方法
protected Integer initialValue() {
return 0;
}
};
public static void main(String[] args) {
for (int i = 0; i < 5; i++) {
// 这里使用的是Thread带参数的构造方法启动5个子线程,参找Thread类的源码
new Thread(new MyThread(i)).start();
}
}
// 实现Runnable接口
static Mythread implements Runnable {
private int index
// 带参数的构造方法
public Mythread(int i) {
this.index = i;
}
// 重写overrirde runnable中的run方法
public void run() {
System.out.println("线程" + index + "的初始value是:" + value.get());
for (int i = 0; i < 10; i++) {
// 这里使用了ThreadLocal中的set方法和get方法
value.set(value.get() + i);
}
System.out.println("线程" + index + "的累加value:" + value.get());
}
}
}
上述代码的执行结果是
线程0的初始value:0
线程3的初始value:0
线程2的初始value:0
线程2的累加value:45
线程1的初始value:0
线程3的累加value:45
线程0的累加value:45
线程1的累加value:45
可见这5个线程中的value值是互相不影响的
ThreadLocal类型变量能达到这种目的的原因
用一句人话来解释:因为ThreadLocal类型的value变量(的值)是绑定在线程上的,即一个线程记录一份这个value。
Thread源码我们可以看到每个线程有个成员变量 ThreadLocal.ThreadLocalMap threadLocals = null;
在ThreadLocal中 获取到这个map 然后map.set(this, value); 这里的this就是ThreadLocal的实例(在我的测试代码中这个实例就是value),value值就是这个实例在这个线程中的值
即相当于每个thread线程中都有一个这样的k-vmap;其中k是每一个ThreadLocal实例,v就是这个ThreadLocal实例在这个线程中的值
ThreadLocal.ThreadLocalMap threadLocals 的存活周期就是线程的生命周期
网友评论