線程範圍內共享數據的示意圖
ThreadLocal的作用和目的: 用於實現線程內的數據共享, 即對於相同的程序代碼, 多個模塊在同一線程中運行時要共享一份數據, 而在另外線程中運行時又共享另外一份數據
每個線程調用全局ThreadLocal對象的set方法, 就相當於往其內部的map中增加一條記錄, key分別是各自的線程, value是各自的set方法傳遞進去的值. 在線程結束時可以調用ThreadLocal.clear()方法, 這樣會更快釋放內存, 不調用也可以, 因爲線程結束後也可以自動釋放相關的ThreadLocal變量
ThreadLocal的應用場景
訂單處理包含一系列操作: 減少庫存量, 增加一條流水臺賬, 修改總賬, 這幾個操作要在同一個事務中完成, 通常也是在同一個線程中進行處理, 如果公司應收款的操作失敗了, 則應該把前面的操作回滾, 否則, 提交所有操作, 這要求這些操作使用相同的數據庫連接對象, 而這些操作的代碼分別位於不同的模塊類中
Struts2的ActionContext, 同一段代碼被不同的線程調用運行時, 該代碼操作的數據是每個線程各自的狀態和數據, 對於不同的線程來說, getContext()方法拿到的對象都不相同, 讀同一個線程來說, 不管調用getContext()方法多少次和在哪個模塊中getContext()方法, 拿到的都是同一個
舉例說明 創建三個線程, 他們都訪問了三個對象, 第一個對象設置值, 第二三個對象取值, 同一個線程設置的值, 只能被相同的線程獲取
實現對ThreadLocal變量的封裝, 讓外界不要直接操作ThreadLocal變量
對基本類型的數據的封裝, 這種應用相對很少見
對對象類型的數據的封裝, 比較常見, 即讓某個類針對不同線程分別創建一個獨立的實例對象
總結: 一個ThreadLocal代表一個變量, 故其中只能放一種數據, 你有兩個變量都要線程範圍內共享, 則要定義兩個ThreadLocal對象, 如果有一百個變量要線程共享呢? 那請定義一個對象來封裝, 然後在ThreadLocal中存儲這一個對象
代碼
package thread;
import java.util.Random;
public class ThreadLocalTest {
public static void main(String[] args) {
for (int i = 0; i < 2; i++) {
new Thread(new Runnable() {
@Override
public void run() {
MyThreadScopeData threadInstance = MyThreadScopeData.getThreadInstance();
String name = new Random().nextInt() + "";
int age = new Random().nextInt();
threadInstance.setName(name);
threadInstance.setAge(age);
System.out.println(Thread.currentThread().getName() + " has put name : " + name);
System.out.println(Thread.currentThread().getName() + " has put age : " + age);
new A().get();
new B().get();
}
}, "thread " + i).start();
}
}
// A B 兩個模塊
static class A {
public void get() {
MyThreadScopeData threadInstance = MyThreadScopeData.getThreadInstance();
System.out.println("A from " + Thread.currentThread().getName() + " get name : " + threadInstance.getName());
System.out.println("A from " + Thread.currentThread().getName() + " get age : " + threadInstance.getAge());
}
}
static class B {
public void get() {
MyThreadScopeData threadInstance = MyThreadScopeData.getThreadInstance();
System.out.println("B from " + Thread.currentThread().getName() + " get name : " + threadInstance.getName());
System.out.println("B from " + Thread.currentThread().getName() + " get age : " + threadInstance.getAge());
}
}
}
class MyThreadScopeData {// 線程範圍內共享類 把線程內需要共享的變量 通通封裝到這個類中 然後在用ThreadLocal 封裝起來
private static ThreadLocal<MyThreadScopeData> map = new ThreadLocal<>();
// 不允許直接創建該對象
private MyThreadScopeData() {
}
// 提供靜態方法 返回和線程相關的實例數據對象
public static MyThreadScopeData getThreadInstance() {
// 這樣外部就看不到ThreadLocal對象了
MyThreadScopeData instance = map.get();
if (instance == null) {
instance = new MyThreadScopeData();
map.set(instance);
}
return instance;
}
private String name;
private int age;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
}