日韩性视频-久久久蜜桃-www中文字幕-在线中文字幕av-亚洲欧美一区二区三区四区-撸久久-香蕉视频一区-久久无码精品丰满人妻-国产高潮av-激情福利社-日韩av网址大全-国产精品久久999-日本五十路在线-性欧美在线-久久99精品波多结衣一区-男女午夜免费视频-黑人极品ⅴideos精品欧美棵-人人妻人人澡人人爽精品欧美一区-日韩一区在线看-欧美a级在线免费观看

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

ThreadLocal入门

發布時間:2023/12/16 编程问答 36 豆豆
生活随笔 收集整理的這篇文章主要介紹了 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) {//把登錄后用戶的信息放在ThreadLocal里面進行保存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<>();@Overridepublic 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) {//把登錄后用戶的信息放在ThreadLocal里面進行保存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>");// session.setAttribute("msg", "請先進行登錄");// response.sendRedirect("http://auth.gulimall.com/login.html");return false;}}@Overridepublic void postHandle(HttpServletRequest request, HttpServletResponse response, Object handler, ModelAndView modelAndView) throws Exception {}@Overridepublic 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就是threadlocalelsecreateMap(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入门的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。