吳密的博客

          每天進步一點點
          posts - 12, comments - 1, trackbacks - 0, articles - 1

          threadLocal--簡單本地緩存

          Posted on 2010-09-10 13:44 xiaolang 閱讀(5650) 評論(0)  編輯  收藏

           

          首先,ThreadLocal 不是用來解決共享對象的多線程訪問問題的,一般情況下,通過ThreadLocal.set() 到線程中的對象是該線程自己使用的對象,其他線程是不需要訪問的,也訪問不到的。各個線程中訪問的是不同的對象。

          另外,說ThreadLocal使得各線程能夠保持各自獨立的一個對象,并不是通過ThreadLocal.set()來實現的,而是通過每個線程中的new 對象 的操作來創建的對象,每個線程創建一個,不是什么對象的拷貝或副本。

          個人版中有一個比較好的應用場景,就是為會員查詢服務做的catche

          在用戶的一次請求中需要多次查詢用戶的卡信息,會多次去調用遠程服務,造成性能上的浪費。如果能在本地做一個簡單的cache,就可以省去遠程調用的開銷,就可以如下圖所示

                                                                           
                                 

          從圖中可以看出,優化后的將一部分cache存在本地,就省去了遠程調用的開銷,從一定程度上減輕了會員核心的壓力。

          Cache
          一般都有一個刷新時間的問題,時間長了,信息可能不準確,時間短了,比較耗費性能。最好的情況是這個cache過期了,不需要使用了,立即把清除,廢棄,這個最理想的情況。

          大家應該知道,用戶從發起請求,到服務器響應的這個過程中,在服務器中是在一個線程中的。如果我們吧查詢出來的東西放到這個線程中,到用戶請求結束時,把這些東西清理掉,應該是一個不錯的cache方案。

          接下來的問題就簡單了,我們只需要把查詢的東西放到threadLocal就可以了。

          看大牛李磊如何實現這一點

          1.   
          首先需要初始化一個threadLocal

              /** 用于將會員信息保存在本地的線程變量*/

              private static ThreadLocal<List<UserInfo>>            localUserInfo      = new ThreadLocal<List<UserInfo>>() {
                                                                                           protected synchronized List<UserInfo> initialValue() {
                                                                                               return new ArrayList<UserInfo>();
                                                                                           } };

           
          2.   
          將查詢出來的東西放到threadLocal,下次查詢的時候,就可以先到threadLocal中取,如果沒有,再調用遠程服務

                  UserInfo userInfo = getFromLocalByCardNo(cardNo);
                  if (userInfo == null) {
                      userInfo = userInfoQueryService.findUserInfo(cardNo);
                      putToLocal(userInfo);
                  }
           
          3
          .用戶的請求結束,把這些內容清理掉。
          為什么要把內容清理掉
          如果一直存放在threadLocal

          1是比較耗費內存;

          2是里面的內容可能是過期的(用戶修改了信息,threadLocal里面沒有更新)

          3請求結束后需要清除ThreadLocal的一個很重要的原因就是,如果使用了線程池,在請求結束后,線程的生命周期還沒有結束,而是放回到池中,這樣下次再使用此線程的時候就會獲得上次的上下文了。

          我們如何做到這一點呢?
          開始之前先給大家介紹一個接口,spring mvc中的。HandlerInterceptor

          這個接口中有三個方法
          preHandle
          在一個該方法會在Controller的方法執行前會被調用,可以使用這個方法來中斷或者繼續執行鏈的處理,當返回true時,處理執行鏈會繼續,當返回false時,則不會去執行Controller的方法
          postHandle
          3個方法會在在controller的方法執行之后,在DispatcherServlet類導向到view進行render之前依次執行。最有用的是使 用postHandleRender方法,因為它有ModelAndView 傳進來,那么我們就可以在render view之前往view中添加額外的model對象,或者對view的去處進行修改(例如下面的HTML還是用Excel來作為View ”例子就是對view進行了更改)

          afterCompletion
          該方法會在render view完成后執行,也可以說在請求過程(request processing)完成之后執行。該方法可以用來清理資源(例如象blackboard building block release context)

          我們只需要在afterCompletion中把本次線程中的存放的信息清理掉,就可以了。
          大家應該記得這一段代碼,初始化threadLocal并不需要這一點,

               static {
                  ThreadLocalCleaner.register(localUserInfo);
                  ThreadLocalCleaner.register(localCertifyStatus);
              }

          讓我們先看下ThreadLocalCleaner的代碼

              /**
               *
          清理所有的線程變量。
               */
              public static void clear() {
                  for (ThreadLocal<?> tl : tls) {
                      tl.remove();
                  }
              }

              /**
               * @param tl
               */
              public static void register(ThreadLocal<?> tl) {
                  tls.add(tl);
              }
           
          方 法register將初始化的ThreadLocal放到一個總的ThreadLocal里,方法clear將總的ThreadLocal里面的內容清 空。我們只需要在HandlerInterceptor實現類中的afterCompletion方法中把clear調用一下就可以了。

           


          只有注冊用戶登錄后才能發表評論。


          網站導航:
           
          主站蜘蛛池模板: 游戏| 杂多县| 都江堰市| 澄迈县| 盖州市| 云梦县| 保靖县| 历史| 邓州市| 余干县| 勃利县| 毕节市| 那曲县| 板桥市| 正镶白旗| 固阳县| 策勒县| 开江县| 鹤山市| 芒康县| 盐边县| 海林市| 广南县| 汝城县| 滦南县| 白沙| 望城县| 五台县| 苗栗县| 长垣县| 高陵县| 讷河市| 罗田县| 石河子市| 广州市| 泸西县| 民丰县| 大兴区| 迁安市| 阿拉尔市| 丰台区|