生活随笔
收集整理的這篇文章主要介紹了
ThreadLocal入门
小編覺得挺不錯的,現在分享給大家,幫大家做個參考.
為提高效率我們經常使用多線程來進行并發操作 有時候會涉及到并發對共享資源進行操作 我們會進行同步使用synchronized或者juc下的原子類型修飾實例變量 但仍然是幾個線程對同一實例變量進行操作 現在希望每一個線程可以有自己私有的變量 通常采用 static ThreadLocal 來存儲每一個線程的局部變量(變量隨著線程的生命周期創建而創建 消失而消失) 使用threadlocal.set來存儲每一個線程的私有變量 threadlocal.get來獲取每一個線程的私有變量 1、threadlocal做到了線程隔離 直接用threadlocal就可以向調用threadlocal的線程里set get不會set錯 get錯
private static final ThreadLocal local
< TsLog
>= new ThreadLocal
< TsLog
> ( ) ; private PrintWriter
getWriter ( ) throws IOException
{ 自動根據調用該函數的線程 即Thread
. currentThread獲取其私有變量TsLog log
= local
. get ( ) ; if ( null
== log
) { log
= new TsLog ( "D://" + Thread
. currentThread ( ) . getName ( ) + "-log.txt" ) ; local
. set ( log
) ; } return log
; }
入門小場景
為每一個線程創建一個自己的Log日志
public class ClientThread extends Thread
{ public void run ( ) { try { PrintWriter printWriter
= new PrintWriter ( new FileWriter ( new File ( "D://" + Thread
. currentThread ( ) . getName ( ) + "-log.txt" ) ) ) ; for ( int i
= 0 ; i
< 10 ; i
++ ) { printWriter
. write ( 'c' ) ; } printWriter
. close ( ) ; } catch ( IOException e
) { e
. printStackTrace ( ) ; } }
}
class Tst { public static void main ( String
[ ] args
) { for ( int i
= 0 ; i
< 3 ; i
++ ) { new ClientThread ( ) . start ( ) ; } }
}
分析題目 就是做到線程隔離 各個線程不再共享變量 我們看到我們不使用threadlocal也做到了 不操作共享變量 因為當前的操做對象不涉及賦值 不涉及++ 本身就是一個線程一個變量
然而threadlocal就只有避免使用synchronized,守護模式等關鍵字 以防不熟練造成死鎖 這一個優點么? 上面的demo如果想讓我們涉及上下文 比如在當前線程的整個功能流暢的下一個功能中 想從當前的線程獲取剛才創建的pritntWriter繼續操作 我們發現無法獲取上下文
場景2
首先要知道一個請求 對應一個線程 要想在之后的整個業務流程中 都使用當前這個線程里的字段 值 比如session cookie 我們從什么地方頻繁的獲取 整個業務流程都需要的值 使用攔截器 攔截所有的請求 使用threadlocal 每一個請求的登錄信息都放在 threadlocal里面 便于之后的請求中頻繁的獲取值
public static ThreadLocal
< MemberResponseVo
> loginUser
= new ThreadLocal < > ( ) ; MemberResponseVo attribute
= ( MemberResponseVo
) request
. getSession ( ) . getAttribute ( LOGIN_USER
) ; if ( attribute
!= null ) { loginUser
. set ( attribute
) ;
package com. xunqi. gulimall. order. interceptor ; import com. xunqi. common. vo. MemberResponseVo ;
import org. springframework. stereotype. Component ;
import org. springframework. util. AntPathMatcher ;
import org. springframework. web. servlet. HandlerInterceptor ;
import org. springframework. web. servlet. ModelAndView ; import javax. servlet. http. HttpServletRequest ;
import javax. servlet. http. HttpServletResponse ;
import java. io. PrintWriter ;
import static com. xunqi. common. constant. AuthServerConstant . LOGIN_USER
; @Component
public class LoginUserInterceptor implements HandlerInterceptor { public static ThreadLocal < MemberResponseVo > loginUser
= new ThreadLocal < > ( ) ; @Override public boolean preHandle ( HttpServletRequest request
, HttpServletResponse response
, Object handler
) throws Exception { String uri
= request
. getRequestURI ( ) ; AntPathMatcher antPathMatcher
= new AntPathMatcher ( ) ; boolean match
= antPathMatcher
. match ( "/order/order/status/**" , uri
) ; boolean match1
= antPathMatcher
. match ( "/payed/notify" , uri
) ; if ( match
|| match1
) { return true ; } MemberResponseVo attribute
= ( MemberResponseVo ) request
. getSession ( ) . getAttribute ( LOGIN_USER
) ; if ( attribute
!= null ) { loginUser
. set ( attribute
) ; return true ; } else { response
. setContentType ( "text/html;charset=UTF-8" ) ; PrintWriter out
= response
. getWriter ( ) ; out
. println ( "<script>alert('請先進行登錄,再進行后續操作!');location.href='http://auth.gulimall.com/login.html'</script>" ) ; return false ; } } @Override public void postHandle ( HttpServletRequest request
, HttpServletResponse response
, Object handler
, ModelAndView modelAndView
) throws Exception { } @Override public void afterCompletion ( HttpServletRequest request
, HttpServletResponse response
, Object handler
, Exception ex
) throws Exception { }
}
我們想在之后的流程里使用 值 就用 LoginUserInterceptor.loginUser.get();來獲取封裝的請求體 并且隨時調用 以供業務使用
結構
并不是從threadlocal中存儲或獲取值 threadlocal類中 真正的boss是thread 在每一個線程類內部都有一個threadlocalMap類型的map 就向下圖代碼所示 調用Threadlocal.get() 其實是currentThread.getMap(); 返回的map來進行值操作 里面就是threadlocalMap
為了做到線程之間隔離 結構應該是 threadlocal內部維護entry[]數組 線程作為key跟hashmap一樣經過一個算法 然后占到對應坑位? 但其實并不是 由于線程遠比threadlocal數量多 用threadlocal維護thread entery的 數量太多 在遍歷數組找到對應<key,value>等功能上都太慢
在threadLocal類當中含有set方法 向里面放入的是<threadlocal,value> thread維護threadlocal所以要先知道threadlocal.set方法在那個線程被調用 然后獲取該線程內部所維護的map 向其中set
{ ThreadLocal. ThreadLocalMap threadLocals
= null ; ThreadLocal. ThreadLocalMap inheritableThreadLocals
= null ; }
public void set ( T value
) { Thread t
= Thread . currentThread ( ) ; ThreadLocalMap map
= getMap ( t
) ; if ( map
!= null ) map
. set ( this , value
) ; this 就是threadlocal
else createMap ( t
, value
) ; } ThreadLocalMap getMap ( Thread t
) { return t
. threadLocals
; }
map.set()
private void set ( ThreadLocal < ? > key
, Object value
) { Entry [ ] tab
= table
; int len
= tab
. length
; int i
= key
. threadLocalHashCode
& ( len
- 1 ) ; for ( Entry e
= tab
[ i
] ; e
!= null ; e
= tab
[ i
= nextIndex ( i
, len
) ] ) { ThreadLocal < ? > k
= e
. get ( ) ; if ( k
== key
) { e
. value
= value
; return ; } if ( k
== null ) { replaceStaleEntry ( key
, value
, i
) ; return ; } } tab
[ i
] = new Entry ( key
, value
) ; int sz
= ++ size
; if ( ! cleanSomeSlots ( i
, sz
) && sz
>= threshold
) rehash ( ) ; }
threadloacal get方法
public T
get ( ) { Thread t
= Thread.currentThread
( ) ; ThreadLocalMap map
= getMap
( t
) ; if ( map
!= null
) { ThreadLocalMap.Entry e
= map.getEntry
( this
) ; if ( e
!= null
) { @SuppressWarnings
( "unchecked" ) T result
= ( T
) e.value
; return result
; } } return setInitialValue
( ) ; }
總結
以上是生活随笔 為你收集整理的ThreadLocal入门 的全部內容,希望文章能夠幫你解決所遇到的問題。
如果覺得生活随笔 網站內容還不錯,歡迎將生活随笔 推薦給好友。