什麼是ThreadLocal?首先要說明的一點是ThreadLocal並非一個Thread,而是Thread的局部變量。在JDK 1.2的版本中就提供java.lang.ThreadLocal,ThreadLocal爲解決多線程程序的併發問題提供了一種新的思路。使用這個工具類能夠很簡潔地編寫出優美的多線程程序。當使用ThreadLocal維護變量時,ThreadLocal爲每一個使用該變量的線程提供獨立的變量副本,因此每個線程均可以獨立地改變本身的副本,而不會影響其它線程所對應的副本。下面咱們就來看看ThreadLocal的初步內容:java
多線程安全性解決方案web
①進行同步控制synchronized效率下降 併發變同步(串行) ,lock(串行)控制靈活spring
②使用ThreadLocal 本地線程 每一個線程一個變量副本(各不相干)數據庫
兩種線程安全方案的差別安全
歸納起來講,對於多線程資源共享的問題,同步機制採用了「以時間換空間」的方式,而 ThreadLocal採用了「以空間換時間」的方式。前者僅提供一份變量,讓不一樣的線程排隊 訪問,然後者爲每個線程都提供了一份變量,所以能夠同時訪問而互不影響,ThreadLocal佔用內存較大,可是速度快,而線程同步相對內存佔用小,可是速度慢。若是在內存比較充足的狀況,對併發部分的執行效率要求很高的話,那麼就是ThreadLocal登場的時候了。通常狀況下用同步機制仍是居多的。session
理解ThreadLocal的原理多線程
每一個ThreadLocal對象內部有個ThreadLocalMap,當線程訪問ThreadLocal對象時,會在線程內部的ThreadLocalMap新建一個Entry,這樣的話每一個線程都有一個對象的副本,保證了併發場景下的線程安全。我理解ThreadLocal的使用場景是某些對象在多線程併發訪問時可能出現問題,好比使用SimpleDataFormat的parse()方法,內部有一個Calendar對象,調用SimpleDataFormat的parse()方法會先調用Calendar.clear(),而後調用Calendar.add(),若是一個線程先調用了add()而後另外一個線程又調用了clear(),這時候parse()方法解析的時間就不對了,咱們就能夠用ThreadLocal<SimpleDataFormat>來解決併發修改的問題。另外一種場景是Spring事務,事務是和線程綁定起來的,Spring框架在事務開始時會給當前線程綁定一個Jdbc Connection,在整個事務過程都是使用該線程綁定的connection來執行數據庫操做,實現了事務的隔離性。Spring框架裏面就是用的ThreadLocal來實現這種隔離,代碼以下所示:併發
public abstract class TransactionSynchronizationManager {框架
//線程綁定的資源,好比DataSourceTransactionManager綁定是的某個數據源的一個Connection,在整個less
//事務執行過程當中 都使用同一個Jdbc Connection
private static final ThreadLocal<Map<Object, Object>> resources = new NamedThreadLocal<>("Transactional resources");
//事務註冊的事務同步器
private static final ThreadLocal<Set<TransactionSynchronization>> synchronizations = new NamedThreadLocal<>("Transaction synchronizations");
//事務名稱
private static final ThreadLocal<String> currentTransactionName = new NamedThreadLocal<>("Current transaction name");
//事務只讀屬性
private static final ThreadLocal<Boolean> currentTransactionReadOnly = new NamedThreadLocal<>("Current transaction read-only status");
//事務隔離級別
private static final ThreadLocal<Integer> currentTransactionIsolationLevel = new NamedThreadLocal<>("Current transaction isolation level");
//事務同步開啓
private static final ThreadLocal<Boolean> actualTransactionActive = new NamedThreadLocal<>("Actual transaction active"); }
不是說一遇到併發場景就用ThreadLocal來解決,咱們還能夠用synchronized或者鎖來實現線程安全,ThreadLocal使用不當時會引發內存泄露的問題
ThreadLocal就是變量在不一樣線程上的副本,不一樣線程不共享,因此對變量改動時就不須要考慮線程間同步的問題了
ThreadLocal在web應用開發中是一種很常見的技巧,當web端採用無狀態寫法時(好比stateless session bean和spring默認的singleton),就能夠考慮把一些變量放在ThreadLocal中
舉個簡單例子,你有兩個方法A和B都要用到變量userId,又不想傳來傳去,一個很天然的想法就是把userId設爲成員變量,可是在無狀態時,這樣作就極可能有問題,由於多個request在同時使用同一個instance,userId在不一樣request下值是不同的,就會出現邏輯錯誤
但因爲同一個request下通常都是處於同一個線程,若是放在ThreadLocal的話,這個變量就被各個方法共享了,而又不影響其餘request,這種狀況下,你能夠簡單把它理解爲是一種沒有反作用的成員變量
咱們知道在通常狀況下,只有無狀態的Bean才能夠在多線程環境下共享,在Spring中, 絕大部分Bean均可以聲明爲singleton做用域。就是由於Spring對一些Bean(如RequestContextHolder、 TransactionSynchronizationManager、LocaleContextHolder等)中非線程安全狀態採用 ThreadLocal進行處理,讓它們也成爲線程安全的狀態。