4 回答

TA貢獻1812條經驗 獲得超5個贊
將 ThreadLocal 視為由同一線程執行的代碼的某種“內存緩存”。完全相同的線程。在不同線程上執行的代碼之間共享 ThreadLocal 是一個壞主意。
Tha javadoc 明確指出:
此類提供線程局部變量。這些變量不同于它們的正常對應變量,因為每個訪問一個(通過它的 get 或 set 方法)的線程都有它自己的、獨立初始化的變量副本。ThreadLocal 實例通常是希望將狀態與線程相關聯的類中的私有靜態字段(例如,用戶 ID 或事務 ID)。
換句話說:使用 ThreadLocals 的目標是為在不同線程中運行的“每個”代碼提供“線程特定”數據。
另一方面,ExecutorService 首先是一個接口:您根本不知道它是由單個線程提供支持,還是(更可能)由多個線程提供支持。
換句話說:使用 ExecutorService 會很快導致多個不同的線程運行您的 Runnables/Tasks。然后您將在這些多個線程中共享您的 ThreadLocal。
因此,“危險”可能是錯誤的詞。使用 ThreadLocal的目標是擁有每個線程的存儲,而 ExecutorService 是關于由未知數量的線程執行的代碼。這兩件事根本不能很好地結合在一起。
重點是不同的:一個概念強調與非常具體的“活動”相關的長期存在的線索。另一個概念是關于使用未知數量的無名線程執行小型獨立活動。

TA貢獻2003條經驗 獲得超2個贊
需要注意的是,您的多次運行Runnable
可能會在不同的線程上執行。執行器服務可以由單個線程支持,但也可以由線程池支持。在您的后續執行中Runnable
,不同的線程將訪問不同的ThreadLocal
.
因此,您當然可以在ThreadLocal
. Runnable
但它不太可能有用,因為通常 a 的目的ThreadLocal
是保持一個值一段時間。相反,aRunnable
通常應該是短暫的。
所以,不,通常將 aThreadLocal
與線程池一起使用是沒有意義的。

TA貢獻1848條經驗 獲得超10個贊
ThreadLocal 將產生不確定的結果——因為我們不能保證給定 userId 的每個 Runnable 操作在每次執行時都由同一個線程處理。
在發布的代碼示例中,上面的參數是無效的,因為該ThreadLocal
值是在run()
被調用時設置的,因此同一塊中的任何后續get()
都是確定性的,無論使用ExecutorService
.
然后從另一個調用不是確定性set(new Context())
的,因為您無法控制正在執行的。Runnable A
get()
Runnable B
Thread
Runnable
假設返回的對象get()
可以是任何東西,除非你知道它最后一次設置的時間。

TA貢獻1860條經驗 獲得超9個贊
ThreadLocal 用于在設置變量后在該線程中緩存變量。所以下次你想訪問它時,你可以直接從 ThreadLocal 中獲取它,而無需初始化。
由于您設置它threadLocal.set(obj)
并通過線程內訪問它threadLocal.get()
,因此您直接獲得線程安全保證。
但是,如果您不明確清除緩存,事情可能會變得丑陋threadLocal.remove()
。
在線程池中,排隊的任務會被線程一一處理,大部分時間任務應該是獨立的,但是線程范圍緩存threadLocal如果忘記清除它會使后面的任務依賴于它之前的任務首先在處理下一個任務之前;
緩存的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;
}
}
添加回答
舉報