一、何為ThreadLocal
1、ThreadLocal的含義
ThreadLocal宽堆,即線程變量,是一個以ThreadLocal對象為鍵茸习,任意對象為值的存儲結(jié)構(gòu)畜隶。這個結(jié)構(gòu)被附帶在線程上,也就是說一個線程可以根據(jù)一個ThreadLocal對象查詢到綁定在這個線程上的一個值。
2籽慢、ThreadLocal的實用意義
每個線程擁有自己的局部變量毫無疑問比使用全局變量好浸遗,因為局部的變量只有縣城自己能看見,而不會影響到其他線程嗡综。ThreadLocal本身能被多個線程共享使用乙帮,并且又能達到線程安全的目的。常用的就是get极景、set察净、initialValue等方法。
3盼樟、使用ThreadLocal前你必須知道的
JDK建議將ThreadLocal定義為private static類型氢卡。
ThreadLocal最常用的地方就是為每個線程綁定一個數(shù)據(jù)連接、HTTP請求晨缴、用戶身份信息等译秦,這樣一個線程的所有調(diào)用到的方法都可以非常方便地訪問這些資源。
二击碗、ThreadLocal的使用
1筑悴、創(chuàng)建和初始化ThreadLocal
我們只需直接用new的方式創(chuàng)建ThreadLocal變量,并使用一個泛型參數(shù)指定ThreadLocal的類型稍途。如果ThreadLocal的初始值不被設(shè)定阁吝,它的默認初始值將被設(shè)定為null,如果要更改初始值械拍,我們通常有兩種方法:
- 使用實現(xiàn)了initialValue的匿名內(nèi)部類
- 使用ThreadLocal的類方法withInitial()突勇,參數(shù)使用lambda表達式
這兩種方法都是在new一個ThreadLocal對象時使用。
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>();
//如果要更改初始值
//方法一 需要一個繼承了ThreadLocal父類,重寫了initialValue方法的類坷虑,用它來創(chuàng)建ThreadLocal類對象
private static ThreadLocal<Integer> threadLocal = new ThreadLocal<>(){ //這里使用匿名內(nèi)部類
@Override
protected Integer initialValue() {
return 200; //初始化的值由null變成200
}
};
//方法二 使用withIntial方法
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->200);
2甲馋、獲取和設(shè)置ThreadLocal的值
使用get和set方法即可
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->200);
//獲取值
System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());//get獲取值
//設(shè)置值
threadLocal.set(99);//set設(shè)置值
三、ThreadLocal的一個簡單示例
public class ThreadLocalTest {
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->1);
public static void main(String[] args) {
for(int i=0;i<5;i++){
new Thread(new MyRun()).start();
}
}
//內(nèi)部類
public static class MyRun implements Runnable{
@Override
public void run() {
Integer left = threadLocal.get();
System.out.println(Thread.currentThread().getName()+"得到了-->"+left);
threadLocal.set(left-1);
System.out.println(Thread.currentThread().getName()+"還剩下-->"+threadLocal.get());
}
}
}
四迄损、ThreadLocal的上下文環(huán)境分析
注意定躏,在ThreadLocal的使用中,get到的值的來源有以下規(guī)律
- 在構(gòu)造器中試圖get:哪個線程調(diào)用了構(gòu)造器就get到哪個線程的ThreadLocal的值
- 在run方法中或其調(diào)用的方法中試圖get:get該線程自己的ThreadLocal的值
以下是一個示例
public class ThreadLocalTest {
private static ThreadLocal<Integer> threadLocal = ThreadLocal.withInitial(()->1);
public static void main(String[] args) {
new Thread(new MyRun()).start(); //如果構(gòu)造器中有讀寫ThreadLocal,這里設(shè)置的是main線程
}
//內(nèi)部類
public static class MyRun implements Runnable{
public MyRun() {
//構(gòu)造器是哪里調(diào)用就屬于哪里芹敌。這里是main調(diào)用的共屈,所以設(shè)置和打印的都是main的ThreadLocal的值
threadLocal.set(100);
System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
}
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"-->"+threadLocal.get());
//new Thread(new MyRun()).start(); //如果在這里又new一個MyRun對象,就要調(diào)用構(gòu)造器党窜,其中set和get的拗引,就是這里這個線程的ThreadLocal的值
}
}
}
五、ThreadLocal和Synchronized的對比
ThreadLocal和Synchronized都是為了解決多線程中相同變量的訪問沖突問題幌衣,不同的點是
- Synchronized是通過線程等待矾削,犧牲時間來解決訪問沖突
- ThreadLocal是通過每個線程單獨一份存儲空間壤玫,犧牲空間來解決沖突,并且相比于Synchronized哼凯,ThreadLocal具有線程隔離的效果欲间,只有在線程內(nèi)才能獲取到對應(yīng)的值,線程外則不能訪問到想要的值断部。
正因為ThreadLocal的線程隔離特性猎贴,使他的應(yīng)用場景相對來說更為特殊一些。在android中Looper蝴光、ActivityThread以及AMS中都用到了ThreadLocal她渴。當(dāng)某些數(shù)據(jù)是以線程為作用域并且不同線程具有不同的數(shù)據(jù)副本的時候,就可以考慮采用ThreadLocal蔑祟。