目录
概述
在java学习生涯中可能很多人都会听到ThreadLocal变量,从字面上理解ThreadLocal就是“线程局部变量”的意思。简单的说就是,一个ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的(每个线程都只能看到自己线程的值)。可能一开始把这句话放出来很难理解,那我们就继续往后面看吧。
API介绍
再学习一个类之前我们需要了解一个类的API,这也是我们学习类的入口。而ThreadLocal类的API相当简单。
在这里面比较重要的就是,get、set、remove了,这三个方法是对这个变量进行操作的关键。set用于赋值操作,get用于获取变量中的值,remove就是删除当前这个变量的值。需要注意的是initialValue方法会在第一次调用时被触发,用于初始化当前变量值,例如在下列代码中我们需要创建一个ThreadLocal<Connection>,用于创建一个与线程绑定的Connection对象:
ThreadLocal<Connection> connection = new ThreadLocal<Connection>(){
public Connection initialValue(){
return DriverManager.getConnection(...);
}
};
为什么我们将ThreadLocal说成变量,我们姑且可以这么理解,每个ThreadLocal实例中都可以保存一个值(基本数据类型值或者引用类型的引用值),而内部保存的值是可以修改的,而这样的特性与变量的特性及其相似,变量不就是用来保存一个值的吗?
也就是说每一个ThreadLocal实例就类似于一个变量名,不同的ThreadLocal实例就是不同的变量名,它们内部会存有一个值(暂时这么理解)在后面的描述中所说的“ThreadLocal变量或者是线程变量”代表的就是ThreadLocal类的实例。
这里还需要介绍一下initialValue方法,我么都知道在Java中成员变量都会有默认值,而ThreadLocal做变量也会有默认值,那我们可以通过重写initialValue方法指定ThreadLocal变量的初始值。默认情况下initialValue返回的是null。
ThreadLocal的理解
说完了ThreadLocal类的API了,那我们就来动手实践一下了,来理解前面没有理解的那句话:一个ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的(每个线程都只能看到自己线程的值)
public class ThreadLocalTest {
private static ThreadLocal<Integer> num = new ThreadLocal<Integer>() {
// 重写这个方法,可以修改“线程变量”的初始值,默认是null
@Override
protected Integer initialValue() {
return 0;
}
};
public static void main(String[] args) {
// 创建一号线程
new Thread(new Runnable() {
@Override
public void run() {
// 在一号线程中将ThreadLocal变量设置为1
num.set(1);
System.out.println("一号线程中ThreadLocal变量中保存的值为:" + num.get());
}
}).start();
// 创建二号线程
new Thread(new Runnable() {
@Override
public void run() {
num.set(2);
System.out.println("二号线程中ThreadLocal变量中保存的值为:" + num.get());
}
}).start();
//为了让一二号线程执行完毕,让主线程睡500ms
try {
Thread.sleep(500);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
System.out.println("主线程中ThreadLocal变量中保存的值:" + num.get());
}
}
稍微解释一下上面的代码:
在类中创建了一个静态的“ThreadLocal变量”,在主线程中创建两个线程,在这两个线程中分别设置ThreadLocal变量为1和2。然后等待一号和二号线程执行完毕后,在主线程中查看ThreadLocal变量的值。
程序结果及分析
程序结果重点看的是主线程输出的是0,如果是一个普通变量,在一号线程和二号线程中将普通变量设置为1和2,那么在一二号线程执行完毕后在打印这个变量,输出的值肯定是1或者2(到底输出哪一个由操作系统的线程调度逻辑有关)。但使用ThreadLocal变量通过两个线程赋值后,在主线程程中输出的却是初始值0。在这也就是为什么“一个ThreadLocal在一个线程中是共享的,在不同线程之间又是隔离的”,每个线程都只能看到自己线程的值,这也就是ThreadLocal的核心作用:实现线程范围的局部变量。
ThreadLocal的原理分析
老规矩我们还是将最后结论摆在前面,每个Thread对象都有一个ThreadLocalMap,当创建一个ThreadLocal的时候,就会将该ThreadLocal对象添加到该Map中,其中键就是ThreadLocal,值可以是任意类型。这句话看不懂很正常,等我们一起看完源码以后就明白了。
此时就需要纠正前面提到的错误观点了,前面我们的理解是所有的常量值或者是引用类型的引用都是保存在ThreadLocal实例中的,但实际上不是的,这种说法只是让我们更好的理解ThreadLocal变量这个概念。向ThreadLocal存入一个值,实际上是向当前线程对象中的ThreadLocalMap存入值,ThreadLocalMap我们可以简单的理解成一个Map,而向这个Map存值的key就是ThreadLocal实例本身。
也就是说,想要存入的ThreadLocal中的数据实际上并没有存到ThreadLocal对象中去,而是以这个ThreadLocal实例作为key存到了当前线程中的一个Map中去了,获取ThreadLocal的值时同样也是这个道理。这也就是为什么ThreadLocal可以实现线程之间隔离的原因了。
总结
ThreadLocal的作用:实现线程范围内的局部变量,即ThreadLocal在一个线程中是共享的,在不同线程之间是隔离的。
ThreadLocal的原理:ThreadLocal存入值时使用当前ThreadLocal实例作为key,存入当前线程对象中的Map中去。最开始在看源码之前,我以为是以当前线程对象作为key将对象存入到ThreadLocal中的Map中去....