ThreadLocal可以使每个线程保存自己的一些私有数据,起到线程隔离的作用。打个比方,可以将其比喻成大型超市里的公共储物柜,每个人都可以使用,但是储物柜又分为好多小箱子来保证每个顾客有属于自己的存储空间,只能存取自己的物品。
下面来看看ThreadLocal的使用。
1.ThreadLocal.get()和ThreadLocal.set()
创建一个ThreadLocalDemo.java类
public class ThreadLocalDemo {
public static ThreadLocal t1 = new ThreadLocal();
public static void main(String[] args){
if(t1.get()== null){
System.out.println("还没有放入数据");
t1.set("已放入值");
t1.set("再次放入覆盖");
}
System.out.println(t1.get());
System.out.println(t1.get());
}
}
运行后,可以看到结果如下:
从运行结果可以看到,第一次调用t1对象的get()返回的是null,通过调用set()后赋值后并取出打印到控制台上,并且从代码中看到,第二次set()方法赋值会覆盖掉第一次的值,所以get取出的值为最后一次set的值。
ThreadLocal解决的是变量在不同线程间的隔离性,也就是不同线程拥有自己的值,不同线程的值是可以放到ThreadLocal类中进行保存的。
2.验证线程变量的隔离性
首先创建一个全局的ThreadLocal类:
public class Tool {
public static ThreadLocal t1 = new ThreadLocal();
}
创建两个自定义线程类:
public class ThreadA extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 10; i++){
Tool.t1.set("ThreadA+"+(i+1));
System.out.println("ThreadA获取的值为 "+ Tool.t1.get());
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public class ThreadB extends Thread {
@Override
public void run() {
try {
for (int i = 0; i < 10; i++){
Tool.t1.set("ThreadB+"+(i+1));
System.out.println("ThreadB获取的值为 "+ Tool.t1.get());
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
测试类:
public class ThreadTest {
public static void main(String[] args){
ThreadA a = new ThreadA();
ThreadB b = new ThreadB();
a.start();
b.start();
try {
for (int i = 0; i < 10; i++){
Tool.t1.set("Main+"+(i+1));
System.out.println("Main获取的值为 "+ Tool.t1.get());
Thread.sleep(100);
}
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
运行后,可以看到结果如下:
从测试中可以看到,虽然运行了3个线程,但是每个线程还是可以从ThreadLocal中取出自己的数据。
在文章开始的代码中,在还没有调用ThreadLocal.set()方法赋值时第一次调用get方法,返回结果为null,那么如何使在第一次调用get方法的时候不返回空,而有默认的取值呢。方法是,创建一个类继承自ThreadLocal并重写initialValue()方法。
代码示例如下:
public class ThreadLocalExt extends ThreadLocal {
@Override
protected Object initialValue() {
return "默认取值,第一次调用不再返回空";
}
}
这里就不再展示运行结果了。