国产成人精品18p,天天干成人网,无码专区狠狠躁天天躁,美女脱精光隐私扒开免费观看

Java ThreadLocal的使用詳解

發(fā)布時(shí)間:2021-07-06 11:13 來(lái)源:腳本之家 閱讀:0 作者:KerryWu 欄目: 開(kāi)發(fā)技術(shù)

目錄

      ThreadLocal是線(xiàn)程私有的局部變量存儲容器,可以理解成每個(gè)線(xiàn)程都有自己專(zhuān)屬的存儲容器,用來(lái)存儲線(xiàn)程私有變量。ThreadLocal 在日常開(kāi)發(fā)框架中應用廣泛,但用不好也會(huì )出現各種問(wèn)題,本文就此講解一下。

      1. 應用場(chǎng)景

      ThreadLocal 的常見(jiàn)應用場(chǎng)景有兩種:

      1. 多線(xiàn)程并發(fā)場(chǎng)景中,用來(lái)保障線(xiàn)程安全。
      2. 處理較為復雜的業(yè)務(wù)時(shí),使用ThreadLocal代替參數的顯示傳遞。

      1.1. 保障線(xiàn)程安全

      多線(xiàn)程訪(fǎng)問(wèn)同一個(gè)共享變量的時(shí)候容易出現并發(fā)問(wèn)題,特別是多個(gè)線(xiàn)程對一個(gè)變量進(jìn)行寫(xiě)入的時(shí)候,為了保證線(xiàn)程安全,一般使用者在訪(fǎng)問(wèn)共享變量的時(shí)候需要進(jìn)行額外的同步措施才能保證線(xiàn)程安全性,如:synchronized、Lock之類(lèi)的鎖。

      ThreadLocal是除了加鎖這種同步方式之外的一種,規避多線(xiàn)程訪(fǎng)問(wèn)出現線(xiàn)程不安全的方法。當我們在創(chuàng )建一個(gè)變量后,如果每個(gè)線(xiàn)程對其進(jìn)行訪(fǎng)問(wèn)的時(shí)候訪(fǎng)問(wèn)的都是線(xiàn)程自己的變量,這樣就不會(huì )存在線(xiàn)程不安全問(wèn)題。

      ThreadLocal是JDK包提供的,它提供線(xiàn)程本地變量,如果創(chuàng )建一個(gè)ThreadLocal變量,那么訪(fǎng)問(wèn)這個(gè)變量的每個(gè)線(xiàn)程都會(huì )有這個(gè)變量的一個(gè)副本,在實(shí)際多線(xiàn)程操作的時(shí)候,操作的是自己本地內存中的變量,從而規避了線(xiàn)程安全問(wèn)題。

      1.2. 顯示傳遞參數

      這里舉幾個(gè)例子:

      示例1:獲取接口的當前請求用戶(hù)
      在后臺接口業(yè)務(wù)邏輯的全過(guò)程中,如果需要在多個(gè)地方獲取當前請求用戶(hù)的信息。通常的一種做法就是:在接口請求時(shí),通過(guò)過(guò)濾器、攔截器、AOP等方式,從session或token中獲取當前用戶(hù)信息,存入ThreadLocal中。

      在整個(gè)接口處理過(guò)程中,如果沒(méi)有另外創(chuàng )建線(xiàn)程,都可以直接從ThreadLocal變量中獲取當前用戶(hù),而無(wú)需再從Session、token中驗證和獲取用戶(hù)。這種方案設計不僅提高性能,最重要的是將原本復雜的邏輯和代碼實(shí)現,變得簡(jiǎn)潔明了。例如下面的這個(gè)例子:

      (1)定義ThreadLocal變量:UserProfileThread.java

      public class UserProfileThread {
          private static ThreadLocal<UserProfile> USER_PROFILE_TL =new ThreadLocal<>();
      
          public static void  setUserProfile(UserProfile userProfile){
              USER_PROFILE_TL.set(userProfile);
          }
      
          public static UserProfile getUserProfile() {
              return USER_PROFILE_TL.get();
          }
      
          public static String getCurrentUser() {
              return Optional.ofNullable(USER_PROFILE_TL.get())
                      .map(UserProfile::getUid)
                      .orElse(UserProfile.ANONYMOUS_USER);
          }
      }

      (2)在過(guò)濾器中設置變量值:

         @Override
          public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException {
              UserProfile userProfile = null;
              // ... 驗證和獲取用戶(hù)信息 userProfile
              UserProfileThread.setUserProfile(userProfile);
              filterChain.doFilter(servletRequest, servletResponse);
          }

      (3)獲取當前用戶(hù)信息

      //獲取當前用戶(hù)
      String uid=UserProfileThread.getCurrentUser();
      //獲取當前用戶(hù)對象
      UserProfile user=UserProfileThread.getUserProfile();

      示例2:spring框架中保證數據事務(wù)在同一個(gè)連接下執行

      要想實(shí)現jdbc事務(wù), 就必須是在同一個(gè)連接對象中操作,多個(gè)連接下事務(wù)就會(huì )不可控,需要借助分布式事務(wù)完成。那spring框架如何保證數據庫事務(wù)在同一個(gè)連接下執行的呢?

      DataSourceTransactionManager 是spring的數據源事務(wù)管理器,它會(huì )在你調用getConnection()的時(shí)候從數據庫連接池中獲取一個(gè)connection, 然后將其與ThreadLocal綁定,事務(wù)完成后解除綁定。這樣就保證了事務(wù)在同一連接下完成。

      2. 實(shí)現原理

      ThreadLocal類(lèi)提供set/get方法存儲和獲取value值,但實(shí)際上ThreadLocal類(lèi)并不存儲value值,真正存儲是靠ThreadLocalMap這個(gè)類(lèi)。

      每個(gè)線(xiàn)程實(shí)例都對應一個(gè)TheadLocalMap實(shí)例,我們可以在同一個(gè)線(xiàn)程里實(shí)例化很多個(gè)ThreadLocal來(lái)存儲很多種類(lèi)型的值,這些ThreadLocal實(shí)例分別作為key,對應各自的value,最終存儲在Entry table數組中。
      我們看看ThreadLocal的set方法:

      public class ThreadLocal<T> {
       public void set(T value) {
              Thread t = Thread.currentThread();
              ThreadLocalMap map = getMap(t);
              if (map != null)
                  map.set(this, value);
              else
                  createMap(t, value);
          }
      
          ThreadLocalMap getMap(Thread t) {
              return t.threadLocals;
          }
      
          void createMap(Thread t, T firstValue) {
              t.threadLocals = new ThreadLocalMap(this, firstValue);
          }
          // 省略其他方法
      }

      set的邏輯比較簡(jiǎn)單,就是獲取當前線(xiàn)程的ThreadLocalMap,然后往map里添加KV,K是當前ThreadLocal實(shí)例,V是我們傳入的value。這里需要注意一下,map的獲取是需要從Thread類(lèi)對象里面取,看一下Thread類(lèi)的定義。

      public class Thread implements Runnable {
          ThreadLocal.ThreadLocalMap threadLocals = null;
          //省略其他
      }

      Thread類(lèi)維護了一個(gè)ThreadLocalMap的變量引用。

      因此,我們可以得出如下結論:

      1. 每個(gè)線(xiàn)程是一個(gè)Thread實(shí)例,其內部維護一個(gè)threadLocals的實(shí)例成員,其類(lèi)型是ThreadLocal.ThreadLocalMap。
      2. ThreadLocal本身并不是一個(gè)容器,我們存取的value實(shí)際上存儲在ThreadLocalMap中,ThreadLocal只是作為T(mén)headLocalMap的key。

      3. 注意事項

      ThreadLocal實(shí)例有提供remove()方法,用于回收對象,清除對應的內存占用。這個(gè)方法通常容易被忽略,而導致出現了各種問(wèn)題。如下面幾種:

      • 線(xiàn)程復用:在“獲取接口的當前請求用戶(hù)”的例子中,Tomcat中是通過(guò)線(xiàn)程池來(lái)處理用戶(hù)請求的,而線(xiàn)程池中線(xiàn)程是復用的??隙〞?huì )出現一個(gè)線(xiàn)程前后被不同用戶(hù)的接口請求復用的情況,因此需要對用過(guò)的ThreaLocal變量進(jìn)行覆蓋或清除。
      • 內存溢出:由于ThreadLocalMap的生命周期跟Thread一樣長(cháng),如果創(chuàng )建的ThreadLocal變量很多,即對應的key占用的內存很大,但卻沒(méi)有手動(dòng)刪除,到了一定程度就會(huì )導致內存泄漏。

      以上就是Java ThreadLocal的使用詳解的詳細內容,更多關(guān)于Java ThreadLocal的使用的資料請關(guān)注腳本之家其它相關(guān)文章!

      免責聲明:本站發(fā)布的內容(圖片、視頻和文字)以原創(chuàng )、來(lái)自互聯(lián)網(wǎng)轉載和分享為主,文章觀(guān)點(diǎn)不代表本網(wǎng)站立場(chǎng),如果涉及侵權請聯(lián)系QQ:712375056 進(jìn)行舉報,并提供相關(guān)證據,一經(jīng)查實(shí),將立刻刪除涉嫌侵權內容。

      狠狠色狠狠色综合日日不卡| 国产日韩在线视看第一页| 丰满少妇高潮惨叫久久久一| 亚洲欧美日韩国产成人| 日本高清视频色WWWWWW色| 亚洲夜夜性无码国产盗摄|