亚洲在线久爱草,狠狠天天香蕉网,天天搞日日干久草,伊人亚洲日本欧美

為了賬號安全,請及時綁定郵箱和手機立即綁定
已解決430363個問題,去搜搜看,總會有你想問的

將 ThreadLocal 與 ExecutorService 一起使用是否危險?

將 ThreadLocal 與 ExecutorService 一起使用是否危險?

慕容708150 2022-06-23 17:44:56
我在下面的博客中討論了 ThreadLocals 的概念:https://www.baeldung.com/java-threadlocal它說“不要將 ThreadLocal 與 ExecutorService 一起使用”它說明了以下使用 ThreadLocals 的示例。public class ThreadLocalWithUserContext implements Runnable {      private static ThreadLocal<Context> userContext       = new ThreadLocal<>();    private Integer userId;    private UserRepository userRepository = new UserRepository();     @Override    public void run() {        String userName = userRepository.getUserNameForUserId(userId);        userContext.set(new Context(userName));        System.out.println("thread context for given userId: "          + userId + " is: " + userContext.get());    }         // standard constructor}在帖子的末尾,它提到:如果我們想使用 ExecutorService 并向其提交 Runnable,使用 ThreadLocal 將產生不確定的結果——因為我們不能保證給定 userId 的每個 Runnable 操作在每次執行時都會由同一個線程處理.因此,我們的 ThreadLocal 將在不同的 userId 之間共享。這就是為什么我們不應該將 TheadLocal 與 ExecutorService 一起使用。只有當我們完全控制哪個線程將選擇執行哪個可運行操作時才應該使用它。這個解釋對我來說是一個保鏢。我試圖專門針對這一點在網上做一些研究,但我沒有得到太多幫助,請高手詳細說明一下上述解釋嗎?是作者的觀點還是真正的威脅?
查看完整描述

4 回答

?
ABOUTYOU

TA貢獻1812條經驗 獲得超5個贊

將 ThreadLocal 視為由同一線程執行的代碼的某種“內存緩存”。完全相同的線程。在不同線程上執行的代碼之間共享 ThreadLocal 是一個壞主意。

Tha javadoc 明確指出:

此類提供線程局部變量。這些變量不同于它們的正常對應變量,因為每個訪問一個(通過它的 get 或 set 方法)的線程都有它自己的、獨立初始化的變量副本。ThreadLocal 實例通常是希望將狀態與線程相關聯的類中的私有靜態字段(例如,用戶 ID 或事務 ID)。

換句話說:使用 ThreadLocals 的目標是為在不同線程中運行的“每個”代碼提供“線程特定”數據。

另一方面,ExecutorService 首先是一個接口:您根本不知道它是由單個線程提供支持,還是(更可能)由多個線程提供支持。

換句話說:使用 ExecutorService 會很快導致多個不同的線程運行您的 Runnables/Tasks。然后您將在這些多個線程中共享您的 ThreadLocal。

因此,“危險”可能是錯誤的詞。使用 ThreadLocal的目標是擁有每個線程的存儲,而 ExecutorService 是關于由未知數量的線程執行的代碼。這兩件事根本不能很好地結合在一起。

重點是不同的:一個概念強調與非常具體的“活動”相關的長期存在的線索。另一個概念是關于使用未知數量的無名線程執行小型獨立活動。


查看完整回答
反對 回復 2022-06-23
?
湖上湖

TA貢獻2003條經驗 獲得超2個贊

需要注意的是,您的多次運行Runnable可能會在不同的線程上執行。執行器服務可以由單個線程支持,但也可以由線程池支持。在您的后續執行中Runnable,不同的線程將訪問不同的ThreadLocal.

因此,您當然可以ThreadLocalRunnable但它不太可能有用,因為通常 a 的目的ThreadLocal是保持一個值一段時間。相反,aRunnable通常應該是短暫的。

所以,不,通常將 aThreadLocal與線程池一起使用是沒有意義的。


查看完整回答
反對 回復 2022-06-23
?
慕桂英546537

TA貢獻1848條經驗 獲得超10個贊

ThreadLocal 將產生不確定的結果——因為我們不能保證給定 userId 的每個 Runnable 操作在每次執行時都由同一個線程處理。

在發布的代碼示例中,上面的參數是無效的,因為該ThreadLocal值是在run()被調用時設置的,因此同一塊中的任何后續get()都是確定性的,無論使用ExecutorService.

然后從另一個調用不是確定性set(new Context())的,因為您無法控制正在執行的。Runnable Aget()Runnable BThreadRunnable

假設返回的對象get()可以是任何東西,除非你知道它最后一次設置的時間。


查看完整回答
反對 回復 2022-06-23
?
慕碼人2483693

TA貢獻1860條經驗 獲得超9個贊

ThreadLocal 用于在設置變量在該線程中緩存變量。所以下次你想訪問它時,你可以直接從 ThreadLocal 中獲取它,而無需初始化。

由于您設置它threadLocal.set(obj)并通過線程內訪問它threadLocal.get(),因此您直接獲得線程安全保證。

但是,如果您不明確清除緩存,事情可能會變得丑陋threadLocal.remove()。

  1. 在線程池中,排隊的任務會被線程一一處理,大部分時間任務應該是獨立的,但是線程范圍緩存threadLocal如果忘記清除它會使后面的任務依賴于它之前的任務首先在處理下一個任務之前;

  2. 緩存的threadLocals 不會立即被 gc-ed(在某個未知的時刻 - 超出您的控制),因為它們的鍵是WeakReference,這可能會在您不知情的情況下導致 OOM。

remove()一個簡單的演示,用于未顯式調用導致 OOM的這種情況。

public class ThreadLocalOne {

    private static final int THREAD_POOL_SIZE = 500;

    private static final int LIST_SIZE = 1024 * 25;


    private static ThreadLocal<List<Integer>> threadLocal = new ThreadLocal<>();


    public static void main(String[] args) throws InterruptedException {


        ExecutorService executorService = Executors.newFixedThreadPool(THREAD_POOL_SIZE);


        for (int i = 0; i < THREAD_POOL_SIZE; i++) {

            executorService.execute(() -> {

                threadLocal.set(getBigList());

                System.out.println(Thread.currentThread().getName() + " : " + threadLocal.get().size());

                // threadLocal.remove(); 

                // explicitly remove the cache, OOM shall not occur;

            });

        }

        executorService.shutdown();

    }


    private static List<Integer> getBigList() {

        List<Integer> ret = new ArrayList<>();

        for (int i = 0; i < LIST_SIZE; i++) {

            ret.add(i);

        }

        return ret;

    }

}


查看完整回答
反對 回復 2022-06-23
  • 4 回答
  • 0 關注
  • 244 瀏覽
慕課專欄
更多

添加回答

舉報

0/150
提交
取消
微信客服

購課補貼
聯系客服咨詢優惠詳情

幫助反饋 APP下載

慕課網APP
您的移動學習伙伴

公眾號

掃描二維碼
關注慕課網微信公眾號