[英]Why should Java ThreadLocal variables be static
我在這里閱讀了 Threadlocal 的 JavaDoc
https://docs.oracle.com/javase/1.5.0/docs/api/java/lang/ThreadLocal.html
它說“ThreadLocal 實例通常是希望將狀態與線程(例如,用戶 ID 或事務 ID)相關聯的類中的私有靜態字段。”
但我的問題是,為什么他們選擇將其設為靜態(通常)——“每線程”狀態讓事情有點混亂,但字段是靜態的?
因為如果它是一個實例級別的字段,那么它實際上是“每線程 - 每實例”,而不僅僅是一個有保證的“每線程”。 這通常不是您要尋找的語義。
通常它持有一些對象,這些對象的范圍限定為用戶對話、Web 請求等。您不希望它們也限定為類的實例。
一個 Web 請求 => 一個 Persistence 會話。
不是一個 Web 請求 => 每個對象一個持久性會話。
要么將其設為靜態,或者如果您試圖避免類中的任何靜態字段 - 將類本身設為單例,然后您就可以安全地使用實例級別 ThreadLocal,只要該單例在全局可用。
不必如此。 重要的是它應該是一個單例。
原因是變量是通過與線程關聯的指針訪問的。 它們就像具有線程作用域的全局變量,因此靜態是最合適的。 這是您在 pthreads 之類的東西中獲得線程本地狀態的方式,所以這可能只是歷史和實現的一個意外。
參考這個,這可以更好地理解。
簡而言之, ThreadLocal
對象就像一個鍵值映射。 當線程調用ThreadLocal
get/set
方法時,它將檢索/存儲映射鍵中的線程對象,以及映射值中的值。 這就是為什么不同的線程具有不同的值副本(您要在本地存儲),因為它駐留在不同的地圖條目中。
這就是為什么您只需要一張地圖即可保留所有值的原因。 盡管不是必需的,但您也可以使用多個映射(不聲明靜態)來保留每個線程對象,這完全是多余的,這就是首選靜態變量的原因。
在每個線程的每個實例上使用 threadlocal 的一個用途是,如果您希望某些內容在對象的所有方法中可見並使其線程安全,而無需像對普通字段那樣同步對它的訪問。
static final ThreadLocal
變量是線程安全的。
static
使 ThreadLocal 變量僅可用於各個線程的多個類。 它是跨多個類的各個線程局部變量的一種全局變量聲明。
我們可以使用以下代碼示例檢查此線程安全性。
CurrentUser
- 在 ThreadLocal 中存儲當前用戶 IDTestService
- 帶有方法的簡單服務 - getUser()
從 CurrentUser 獲取當前用戶。TestThread
- 此類用於創建多個線程並同時設置用戶標識.
public class CurrentUser
public class CurrentUser {
private static final ThreadLocal<String> CURRENT = new ThreadLocal<String>();
public static ThreadLocal<String> getCurrent() {
return CURRENT;
}
public static void setCurrent(String user) {
CURRENT.set(user);
}
}
public class TestService {
public String getUser() {
return CurrentUser.getCurrent().get();
}
}
.
import java.util.ArrayList;
import java.util.List;
public class TestThread {
public static void main(String[] args) {
List<Integer> integerList = new ArrayList<>();
//creates a List of 100 integers
for (int i = 0; i < 100; i++) {
integerList.add(i);
}
//parallel stream to test concurrent thread execution
integerList.parallelStream().forEach(intValue -> {
//All concurrent thread will set the user as "intValue"
CurrentUser.setCurrent("" + intValue);
//Thread creates a sample instance for TestService class
TestService testService = new TestService();
//Print the respective thread name along with "intValue" value and current user.
System.out.println("Start-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());
try {
//all concurrent thread will wait for 3 seconds
Thread.sleep(3000l);
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//Print the respective thread name along with "intValue" value and current user.
System.out.println("End-"+Thread.currentThread().getName()+"->"+intValue + "->" + testService.getUser());
});
}
}
.
運行 TestThread 主類。 輸出 -
Start-main->62->62
Start-ForkJoinPool.commonPool-worker-2->31->31
Start-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-1->87->87
End-main->62->62
End-ForkJoinPool.commonPool-worker-1->87->87
End-ForkJoinPool.commonPool-worker-2->31->31
End-ForkJoinPool.commonPool-worker-3->81->81
Start-ForkJoinPool.commonPool-worker-2->32->32
Start-ForkJoinPool.commonPool-worker-3->82->82
Start-ForkJoinPool.commonPool-worker-1->88->88
Start-main->63->63
End-ForkJoinPool.commonPool-worker-1->88->88
End-main->63->63
...
分析總結
main
執行結束並將當前用戶打印為“62”,並行ForkJoinPool.commonPool-worker-1
執行結束並將當前用戶打印為“87”,並行ForkJoinPool.commonPool-worker-2
執行結束並將當前用戶打印為“ 31", 並行ForkJoinPool.commonPool-worker-3
執行結束並將當前用戶打印為 "81"推理
並發線程能夠檢索正確的用戶 ID,即使它已聲明為“靜態最終 ThreadLocal”
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.