ThreadLocal
執行緒資料共享和安全
1.什么是ThreadLocal?
-
ThreadLocal的作用,可以實作在同一個執行緒資料共享,從而解決多執行緒資料安全問題
當http請求發送到Tomcat服務端時,Tomcat會創建一個執行緒去處理這個http請求,如果是請求servlet,servlet可能又會呼叫其他service,在這些service中,又可能會呼叫dao,去對資料庫進行操作,
在這些資源或者方法的呼叫中,為解決資料安全問題,在這一個執行緒執行的程序中,我們希望有一個資料是共享的,而且是安全的,
應用場景:比如說事務控制,一個執行緒可能涉及到多個service的呼叫,呼叫多個dao,在這程序中,可能對資料庫的多張表進行了操作,這時我們希望在整個業務流程結束之后,再進行一次提交commit,反過來說,在沒有進行提交之前,我們希望始終是一個connection在操作,這樣才能在結束時進行統一的一次提交(在開始操作的時候將自動提交設定為false),
這樣就可以解決同一個請求中,呼叫多個service或者多個dao的需求,這個需求也是開發中必須解決的事務安全問題(事務一致性需求),
ThreadLocal技術就能夠很好地解決這個問題,我們可以在實際開發中使用Filter和ThreadLocal解決事務安全問題,
-
一個ThreadLocal物件可以給當前執行緒關聯一個資料(普通變數,物件,對組)--使用set方法
-
ThreadLocal可以像Map一樣存取資料,key為當前的ThreadLocal物件--使用get方法
-
每一個ThreadLocal物件只能為當前執行緒關聯一個資料,如果要為當前執行緒關聯多個資料,就需要使用多個ThreadLocal物件實體
-
每個ThreadLocal物件實體定義的時候,一般為static型別
-
ThreadLocal中保存的資料,在執行緒銷毀之后,會自動釋放
2.ThreadLocal快速入門
2.1ThreadLocal的類圖
如下:ThreadLocal類中常用的方法有get(),set(),getMap()等,ThreadLocal類中含有一個重要的內部類ThreadLocalMap,ThreadLocalMap類中又含有一個內部類Entry,資料以key-value的形式存放在Entry中,
- ThreadLocal核心的價值就是:在一個執行緒中,以執行緒安全的方式來共享資料,
2.2應用實體
需求: 演示 ThreadLocal (作用:在一個執行緒中, 共享資料(執行緒安全))的使用
Dog:
package com.li.threadlocal;
public class Dog {
}
T1:
package com.li.threadlocal;
public class T1 {
//創建ThreadLocal物件, public static修飾
public static ThreadLocal<Object> threadLocal1 = new ThreadLocal<>();
public static void main(String[] args) {
new Thread(new Task()).start();//啟動一個新的執行緒,注意不是主執行緒
}
//Task是一個執行緒類,同時是一個內部類
public static class Task implements Runnable {
@Override
public void run() {
Dog dog = new Dog();
Pig pig = new Pig();
//給threadLocal1物件放入dog
System.out.println("Task 放入了 dog=" + dog);
threadLocal1.set(dog);
System.out.println("Task 的 run 方法中的執行緒= " + Thread.currentThread().getName());
new T1Service().update();
}
}
}
T1Service:
package com.li.threadlocal;
public class T1Service {
public void update() {
//取出threadLocal1物件關聯的物件
Object o = T1.threadLocal1.get();
//獲取當前執行緒名
String name = Thread.currentThread().getName();
System.out.println("在T1Service 的update()的執行緒是= " + name + ", 取出dog= " + o);
//呼叫了dao-update()方法
new T1DAO().update();
}
}
T1DAO:
package com.li.threadlocal;
public class T1DAO {
public void update() {
//取出執行緒關聯的threadLocal1物件的資料
Object o = T1.threadLocal1.get();
//獲取當前執行緒的名稱
String name = Thread.currentThread().getName();
System.out.println("在T1DAO 的update()的執行緒是= " + name + ", 取出dog= " + o);
}
}
可以看到所有方法中拿到的物件都是同一個:
3.原始碼分析
3.1ThreadLocal的set()
在2.2的應用實體中,我們在T1類中使用了ThreadLocal.set()方法,現在來看看set()方法的底層原始碼:
set()方法關聯的其他方法和屬性:
從上述代碼中我們可以知道set方法的主要作業如下:
public void set(T value) {
//1.獲取當前執行緒
Thread t = Thread.currentThread();
//2.通過執行緒物件,獲取到和此執行緒關聯的ThreadLocalMap
// (ThreadLocalMap是ThreadLocal里的一個靜態內部類*,
// 型別是ThreadLocal$ThreadLocalMap)
ThreadLocalMap map = getMap(t);
//3.如果獲取到的ThreadLocalMap不為空,就將傳入的資料放入map,其中:
// -key為當前的ThreadLocal物件(this) -value為存放的資料,
// 因為key值不能重復(map性質),一個ThreadLocal物件只能存放一個資料
// 如果再賦值,就會替換舊的value值
if (map != null)
map.set(this, value);
//4.如果和當前執行緒關聯的ThreadLocalMap為null,
//就創建一個和當前執行緒關聯的ThreadLocalMap,并且將存放的資料作為value放入map,
//這里的key為當前執行緒t(作用是讓執行緒和創建的map關聯起來)
else
createMap(t, value);
}
在2.2應用實體的threadLocal1.set(dog);陳述句旁打上斷點,點擊debug,點擊step over,
如下圖所示,可以看到子執行緒Thread-0中有一個threadLocals屬性(該屬性的型別為ThreadLocalMap),該map中又有一個table屬性(table的型別為Entry[]陣列), table陣列存放Entry對,
這里涉及到的弱參考暫不深入
在Entry對中,以k-v的形式存放資料,key值為 當前的執行緒中 的ThreadLocal物件,value值為存放的資料,
因為map的存放性質,如果在同一個ThreadLocal物件中存放多個value,那么在底層的Entry對中保存的是最近存放的value值,這也是為什么一個ThreadLocal物件只能存放一個值,
執行緒中所有的ThreadLocal物件都被當前執行緒的threadLocals屬性(map)管理,因此無論在哪個方法中,只要能找到對應的執行緒Thread,就對該執行緒關聯的所有ThreadLocal物件中的value值進行操作,
一個執行緒中可以有多個ThreadLocal物件,如果還有其他ThreadLocal物件,使用set方法,存放的就是其他Entry對(key值就是其他的ThreadLocal物件)
存放多個ThreadLocal物件:
3.2ThreadLocal的get()
public T get() {
//先得到當前的執行緒物件
Thread t = Thread.currentThread();
//獲取和當前執行緒物件關聯的ThreadLocalMap
ThreadLocalMap map = getMap(t);
//如果map不為空
if (map != null) {
//就根據當前的呼叫者this(即當前呼叫get方法的ThreadLocal物件),得到對應的Entry
ThreadLocalMap.Entry e = map.getEntry(this);
//如果Entry的值e不為空
if (e != null) {
@SuppressWarnings("unchecked")
//回傳當前ThreadLocal物件關聯的value值
T result = (T)e.value;
return result;
}
}
//如果map為空,就初始化map,并將map和當前執行緒關聯
return setInitialValue();
}
3.3總結
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/539701.html
標籤:其他
