簡體   English   中英

為什么 Java ThreadLocal 變量應該是靜態的

[英]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 中存儲當前用戶 ID
  • TestService - 帶有方法的簡單服務 - 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
...

分析總結

  1. “主”線程啟動並將當前用戶設置為“62”,並行“ForkJoinPool.commonPool-worker-2”線程啟動並將當前用戶設置為“31”,並行“ForkJoinPool.commonPool-worker-3”線程啟動並設置當前用戶為“81”,並行“ForkJoinPool.commonPool-worker-1”線程啟動並將當前用戶設置為“87” 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
  2. 以上所有線程將休眠 3 秒
  3. 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.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM