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 "默認取值,第一次調用不再返回空";
}
}
這裏就不再展示運行結果了。