成人免费xxxxx在线视频软件_久久精品久久久_亚洲国产精品久久久_天天色天天色_亚洲人成一区_欧美一级欧美三级在线观看

100%保證線程安全,還有這種黑科技?

存儲(chǔ)
今天學(xué)習(xí)了ThreadLocal相關(guān)的知識(shí),發(fā)現(xiàn)原來道哥(Doug Lea)也用ThreadLocal。既然大師們都喜歡用的,我們必須得研究起來。大師的背影總是需要追隨。

 本文轉(zhuǎn)載自微信公眾號「Java技術(shù)指北」,作者指北君。轉(zhuǎn)載本文請聯(lián)系Java技術(shù)指北公眾號。

大家好,我是指北君。

今天學(xué)習(xí)了ThreadLocal相關(guān)的知識(shí),發(fā)現(xiàn)原來道哥(Doug Lea)也用ThreadLocal。既然大師們都喜歡用的,我們必須得研究起來。大師的背影總是需要追隨。

那么指北君給大家安排上了,如果你擁有了Java中的ThreadLocal,那麼你可以創(chuàng)建一個(gè)只允許同一個(gè)線程讀寫的變量。因此,即使兩個(gè)線程執(zhí)行了相同的代碼,并且引用了相同的ThreadLocal變量,這兩個(gè)線程也無法看到彼此的ThreadLocal。可以說ThreadLocal提供了一種代碼線程安全的的簡單方法。

下面我們就來看看道哥都用的ThreadLocal。

1 ThreadLocal你來自哪里

  1. Since: 1.2 
  2. Author: Josh Bloch and Doug Lea 

又是并發(fā)大佬們的杰作,膜拜一下。怪不得道哥也愛用,自己設(shè)計(jì)的類總得用用。下面來看看基本內(nèi)容與用法吧。

[[408322]]

2 ThreadLocal原理

首先請看男神們的介紹

“This class provides thread-local variables. These variables differ from their normal counterparts in that each thread that accesses one (via its get or set method) has its own, independently initialized copy of the variable. ThreadLocal instances are typically private static fields in classes that wish to associate state with a thread (e.g., a user ID or Transaction ID).”

“此類提供了thread-local變量。這些變量不同于普通的類似變量,因?yàn)樵L問某個(gè)變量(通過其 get 或 set 方法)的每個(gè)線程都有自有的,獨(dú)立初始化的變量副本,ThreadLocal實(shí)例通常是希望將狀態(tài)與線程(例如,用戶ID或事務(wù)ID)關(guān)聯(lián)的類中的私有靜態(tài)字段。”

通過老爺子們的描述,指北君大概也知道了ThreadLocal的推薦使用場景,

  1. ThreadLocal提供了一種訪問某個(gè)特有變量的方法 訪問到的變量屬于當(dāng)前線程,同一線程在任何地方都能訪問同一個(gè)線程特有變量。
  2. 推薦定義為 private static 類型,但是Doug Lea老爺子在ThreadLocalRandom 和 ReentrantReadWriteLock 中使用了 private static final 類型。(肯定是當(dāng)年寫簡介的時(shí)候手抖了)

2.1 Thread中如何存儲(chǔ)

既然是線程的變量,自然是存在Thread對象中的一個(gè)變量了,但是它是通過ThreadLocal這個(gè)類來維護(hù)的。

  1. //與此線程相關(guān)的ThreadLocal值,由ThreadLocal這個(gè)類維護(hù) 
  2. ThreadLocal.ThreadLocalMap threadLocals = null
  3.  
  4. //與此線程相關(guān)的可繼承的ThreadLocal值,由InheritableThreadLocal類來維護(hù) 
  5. ThreadLocal.ThreadLocalMap inheritableThreadLocals = null

ThreadLocal中有一個(gè)內(nèi)部類來ThreadLocalMap來維護(hù)這些線程本地變量,

  1. static class ThreadLocalMap { 
  2.         //初始容量,2的n次方 
  3.         private static final int INITIAL_CAPACITY = 16;  
  4.          
  5.         //根據(jù)需要調(diào)整數(shù)組大小,2的n次方 
  6.         private Entry[] table
  7.  
  8.         //上面Entry數(shù)組中的元素?cái)?shù)量 
  9.         private int size = 0; 
  10.  
  11.         //The next size value at which to resize  Default to 0 
  12.         private int threshold;  

ThreadLocalMap中的Entry結(jié)構(gòu)如下,是一種key為弱引用(其目的就是Entry對象在GC時(shí)容易回收)的hash map,其中key總是ThreadLocal。

  1. static class Entry extends WeakReference<ThreadLocal<?>> { 
  2.     /** The value associated with this ThreadLocal. */ 
  3.     Object value; 
  4.     Entry(ThreadLocal<?> k, Object v) { 
  5.         super(k); 
  6.         value = v; 
  7.     } 

2.2 常用方法 get,set,remove 詳解

get() 此方法是ThreadLocal最重要的方法之一,該方法返回此線程局部變量的當(dāng)前線程副本中的值。大概可分為以下幾步:

(1) 先獲取當(dāng)前線程,然后再從線程中得到ThreadLocalMap。

(2) 然后使用ThreadLocal對象的threadLocalHashCode進(jìn)行散列計(jì)算,得到一個(gè)數(shù)組的index

(3) 從Table數(shù)組中得到Entry,再對比Entry的key是不是和當(dāng)前的ThreadLocal相等,如果相等就返回此Entry的value

(4) 如果上一步中得到的Entry與當(dāng)前ThreadLocal不相等,則會(huì)在方法getEntryAfterMiss中進(jìn)行遍歷Entry數(shù)組table中的每一個(gè)元素,如果找不到就返回null。而且在遍歷的過程中會(huì)順便清理一下廢棄的Entry。

下面可以看一下get方法的具體代碼。

  1. public T get() { 
  2.     //獲取當(dāng)前線程 
  3.     Thread t = Thread.currentThread();  
  4.  
  5.     //從當(dāng)前線程中獲取ThreadLocalMap 
  6.     ThreadLocalMap map = getMap(t);  
  7.     if (map != null) { 
  8.          
  9.         //獲取map中當(dāng)前ThreadLocal對象對應(yīng)的entry 
  10.         ThreadLocalMap.Entry e = map.getEntry(this);  
  11.         if (e != null) { 
  12.                 @SuppressWarnings("unchecked"
  13.                 T result = (T)e.value; 
  14.                 return result; 
  15.         } 
  16.     } 
  17.     return setInitialValue(); 
  18.  
  19. private Entry getEntry(ThreadLocal<?> key) { 
  20.     //散列計(jì)算得到Entry中當(dāng)前的index 
  21.     int i = key.threadLocalHashCode & (table.length - 1); 
  22.     Entry e = table[i]; 
  23.      
  24.     //如果Entry不是null而且key等于當(dāng)前 
  25.     // ThreadLocal對象則返回此Entry 
  26.     if (e != null && e.get() == key)  
  27.         return e; 
  28.     else 
  29.          
  30.         //Entry==null 或者其key不等于當(dāng)前 
  31.         // ThreadLocal對象,遍歷其余Entry 
  32.         return getEntryAfterMiss(key, i, e); 
  33.  
  34. private Entry getEntryAfterMiss(ThreadLocal<?> keyint i, Entry e) { 
  35.     Entry[] tab = table
  36.     int len = tab.length; 
  37.     while (e != null) { 
  38.         ThreadLocal<?> k = e.get(); 
  39.         if (k == keyreturn e; 
  40.         if (k == null
  41.             //如果遍歷過程中發(fā)現(xiàn)有Entry的KeyNull, 
  42.             // 則清除掉作廢的Entry 
  43.             expungeStaleEntry(i); 
  44.         else 
  45.             //計(jì)算Entry數(shù)組下一個(gè)index 
  46.             i = nextIndex(i, len); 
  47.             e = tab[i]; 
  48.         } 
  49.         return null
  • set(T value) 此方法將此線程局部變量的當(dāng)前線程副本中的值設(shè)置為指定值。

set線程本地變量步驟如下:

(1) 首先依然是獲取此線程的ThreadLocalMap

(2) Map不為null時(shí)往map中插入數(shù)據(jù),否側(cè)創(chuàng)建map并插入數(shù)據(jù)

(3) 具體的set方法依然是先遍歷Entry數(shù)組中所有的的Entry,然后依次對比每個(gè)Entry的key是否等于當(dāng)前ThreadLocal,如果相等則直接替換現(xiàn)有Entry的value。如果Entry的Key為null,則立馬清理廢棄的Entry,并用新的Entry來替換此卡槽。

(4) 如果遍歷完都沒有return,則在在table中相應(yīng)卡槽下新建Entry對象

  1. public void set(T value) { 
  2.     Thread t = Thread.currentThread(); 
  3.     ThreadLocalMap map = getMap(t); 
  4.     if (map != null
  5.         map.set(this, value); 
  6.     else 
  7.         createMap(t, value); 
  8.  } 
  9.   
  10. private void set(ThreadLocal<?> key, Object value) { 
  11.     Entry[] tab = table
  12.     int len = tab.length; 
  13.     int i = key.threadLocalHashCode & (len-1); 
  14.     for (Entry e = tab[i]; 
  15.          e != null
  16.          e = tab[i = nextIndex(i, len)]) { 
  17.         ThreadLocal<?> k = e.get(); 
  18.          
  19.         //如果原Entry的key就是當(dāng)前ThreadLocal對象, 
  20.         // 則直接替換現(xiàn)有value 
  21.         if (k == key) {      
  22.             e.value = value; 
  23.             return
  24.         } 
  25.         if (k == null) { 
  26.              
  27.     // 如果Entry的Keynull, 則直接替換為新的Entry             
  28.             replaceStaleEntry(key, value, i);  
  29.             return
  30.         } 
  31.     } 
  32.     // 如果前面的遍歷沒有return, 
  33.     // 則插入新的Entry對象到對應(yīng)的卡槽     
  34.     tab[i] = new Entry(key, value);  
  35.     int sz = ++size
  36.     if (!cleanSomeSlots(i, sz) && sz >= threshold) 
  37.         rehash(); 

remove() remove則相對簡單,直接遍歷ThreadLocalMap中Entry數(shù)組table,找到對應(yīng)的Entry,將Entry的key置為null,然后再清理相應(yīng)的Entry。

  1. private void remove(ThreadLocal<?> key) { 
  2.     ... 
  3.     for (Entry e = tab[i]; e != null; e = tab[i = nextIndex(i, len)]) { 
  4.         if (e.get() == key) { 
  5.             //Entry 的key置為null 
  6.             e.clear(); 
  7.             // 清理對應(yīng)卡槽, 
  8.             expungeStaleEntry(i);  
  9.             return
  10.         } 
  11.     } 

3 Java中使用的ThreadLocal

Java中有哪些源碼使用了ThreadLocal。

ThreadLocalRandom 中使用計(jì)算nextGaussian值時(shí)有使用到ThreadLocal。

InheritableThreadLocal繼承了ThreadLocal,線程中使用inheritableThreadLocals這個(gè)map存儲(chǔ)線程本地變量。和ThreadLocal的區(qū)別就是子線程依然可以訪問到父線程的線程本地變量,實(shí)際應(yīng)用中也推薦InheritableThreadLocal

ReentrantReadWriteLock中線程讀寫鎖的計(jì)數(shù)器使用了ThreadLocal,其目的是記錄每個(gè)線程獲取讀寫鎖的次數(shù)

  1. static final class ThreadLocalHoldCounter  
  2.         extends ThreadLocal<HoldCounter> { 
  3.     public HoldCounter initialValue() { 
  4.         return new HoldCounter(); 
  5.     } 
  6. //曾經(jīng)的Doug Lea老爺子推薦static field, 
  7. // 而他默默的使用了static final。 

4 如何使用ThreadLocal

ThreadLocal非常適合存儲(chǔ)非線程安全的對象,并且不需要跨線程共享對象。很多需要線程隔離的操作都可以嘗試使用它。

ThreadLocal也非常適合在Web應(yīng)用程序中使用,典型的應(yīng)用就是在Web請求進(jìn)來一開始就將請求狀態(tài)存儲(chǔ)在ThreadLocal中,然后參與處理的任何組件均可訪問該狀態(tài)。

以下是一個(gè)ThreadLocal示例:

具體使用就是配合interceptor或者filter在線程剛開始執(zhí)行的時(shí)候存儲(chǔ)SessionContext,線程執(zhí)行過程中可以隨時(shí)訪問該變量。然后在線程執(zhí)行結(jié)束的時(shí)候再調(diào)用remove()方法移除,防止內(nèi)存泄漏。

  1. public class SessionContextHolder { 
  2.     private static final ThreadLocal<SessionContex> CONTEXHOLDER  
  3.                     = new InheritableThreadLocal<>(); 
  4.      
  5.     public static void remove(){CONTEXHOLDER.remove();}; 
  6.      
  7.     public static SessionContex get(){return CONTEXHOLDER.get();} 
  8.      
  9.     public static void set(SessionContex sessionContex) {CONTEXHOLDER.set(sessionContex);} 

總結(jié)

本文介紹了ThreadLocal的原理以及解析了常用方法的實(shí)現(xiàn)邏輯,以及在ThreadLocal一些應(yīng)用。在一步步梳理的過程中,果然看到了以往忽略的各種細(xì)節(jié),最后給出了一個(gè)小Case。并發(fā)編程大神道哥.李都在用的ThreadLocal,不妨在自己的項(xiàng)目中偷偷用上,保證絲滑舒適。

 

責(zé)任編輯:武曉燕 來源: Java技術(shù)指北
相關(guān)推薦

2020-08-20 07:38:51

Java字符串整形

2021-09-03 11:15:18

場景sql配置

2021-05-06 07:00:28

Excel數(shù)據(jù)技巧

2021-12-27 09:13:51

騰訊京東共同富裕

2021-11-18 07:39:41

Json 序列化Vue

2023-01-26 02:07:51

HashSet線程安全

2024-06-17 00:02:00

線程安全HashMapJDK 1.7

2021-08-29 23:14:06

Windows 11Windows微軟

2024-05-20 13:13:01

線程安全Java

2018-07-12 14:59:44

獵網(wǎng)

2017-08-03 11:36:31

人工智能

2023-10-30 08:16:33

數(shù)據(jù)庫插件Mybatis

2016-11-18 11:38:34

2017-06-24 17:06:42

2019-08-12 07:40:24

華為開發(fā)者黑科技

2022-02-05 23:59:28

智能汽車安全駕駛

2009-09-10 09:54:24

虛擬化LinuxLinux操作系統(tǒng)

2022-06-07 23:28:05

線程安全后端

2022-09-26 13:46:18

Java線程安全

2018-01-25 09:42:56

蘇寧刷臉跟蹤
點(diǎn)贊
收藏

51CTO技術(shù)棧公眾號

主站蜘蛛池模板: 色播av| 欧美一区视频 | 精品伊人久久 | 国产精品午夜电影 | 欧美一区二区三区在线 | 一级片av | 国产在线一区二区 | 国产精品免费一区二区三区四区 | 成人精品鲁一区一区二区 | 欧美日韩一二三区 | 国产成人一区二 | 欧美午夜精品久久久久免费视 | 一区二区亚洲 | 久久精品一区二区 | 一区二区不卡 | 欧美性受xxx | www.啪啪.com| 国产一区二区三区高清 | 一区二区三区四区av | 狠狠亚洲| av色噜噜 | 精品久久久久久亚洲综合网 | 欧美精品一区二区三区在线播放 | 色综合久久久久 | 日韩精品一区二区三区在线播放 | 精品久久久久一区二区国产 | av黄色在线 | 一区二区三区高清在线观看 | 国产一区二区三区久久久久久久久 | 欧美二三区 | 国产精品网页 | 九九免费视频 | 国产精品永久免费观看 | 中文字幕一页二页 | 日韩中文在线观看 | 精品三级在线观看 | 中文字幕一区二区三区乱码图片 | 一级免费毛片 | 一级黄色绿像片 | 久久艹免费视频 | 久久久久久999 |