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

Java ThreadLocal 使用詳解

開發 后端
“該類提供了線程局部 (thread-local) 變量。這些變量不同于它們的普通對應物,因為訪問某個變量(通過其 get 或 set 方法)的每個線程都有自己的局部變量,它獨立于變量的初始化副 本。ThreadLocal 實例通常是類中的 private static 字段,它們希望將狀態與某一個線程(例如,用戶 ID 或事務 ID)相關聯。”

引言

ThreadLocal的官方API解釋為:

“該類提供了線程局部 (thread-local) 變量。這些變量不同于它們的普通對應物,因為訪問某個變量(通過其 get 或 set 方法)的每個線程都有自己的局部變量,它獨立于變量的初始化副 本。ThreadLocal 實例通常是類中的 private static 字段,它們希望將狀態與某一個線程(例如,用戶 ID 或事務 ID)相關聯。”

大概的意思有兩點:

  1. ThreadLocal提供了一種訪問某個變量的特殊方式:訪問到的變量屬于當前線程,即保證每個線程的變量不一樣,而同一個線程在任何地方拿到的變量都是一致的,這就是所謂的線程隔離。

  2. 如果要使用ThreadLocal,通常定義為private static類型,在我看來***是定義為private static final類型。

應用場景

ThreadLocal通常用來共享數據,當你想在多個方法中使用某個變量,這個變量是當前線程的狀態,其它線程不依賴這個變量,你***時間想到的 就是把變量定義在方法內部,然后再方法之間傳遞參數來使用,這個方法能解決問題,但是有個煩人的地方就是,每個方法都需要聲明形參,多處聲明,多處調用。 影響代碼的美觀和維護。有沒有一種方法能將變量像private static形式來訪問呢?這樣在類的任何一處地方就都能使用。這個時候ThreadLocal大顯身手了。

實踐

我們首先來看一段代碼:

  1. mport java.util.HashMap; 
  2.  
  3. import java.util.Map; 
  4.  
  5. public class TreadLocalTest { 
  6.  
  7. // static ThreadLocal<HashMap> threadLocal = new ThreadLocal<HashMap>(){ 
  8.  
  9. // @Override 
  10.  
  11. // protected HashMap initialValue() { 
  12.  
  13. // System.out.println(Thread.currentThread().getName()+”initialValue”); 
  14.  
  15. // return new HashMap(); 
  16.  
  17. // } 
  18.  
  19. // }; 
  20.  
  21. public static class T1 implements Runnable { 
  22.  
  23. private final static Map map = new HashMap(); 
  24.  
  25. int id; 
  26.  
  27. public T1(int id) { 
  28.  
  29. this.id = id; 
  30.  
  31.  
  32. public void run() { 
  33.  
  34. // Map map = threadLocal.get(); 
  35.  
  36. for (int i = 0; i < 20; i++) { 
  37.  
  38. map.put(i, i + id * 100); 
  39.  
  40. try { 
  41.  
  42. Thread.sleep(100); 
  43.  
  44. catch (Exception ex) { 
  45.  
  46.  
  47.  
  48. System.out.println(Thread.currentThread().getName() 
  49.  
  50. + “# map.size()=” + map.size() + ” # ” + map); 
  51.  
  52.  
  53.  
  54. public static void main(String[] args) { 
  55.  
  56. Thread[] runs = new Thread[15]; 
  57.  
  58. T1 t = new T1(1); 
  59.  
  60. for (int i = 0; i < runs.length; i++) { 
  61.  
  62. runs[i] = new Thread(t); 
  63.  
  64.  
  65. for (int i = 0; i < runs.length; i++) { 
  66.  
  67. runs[i].start(); 
  68.  
  69.  
  70.  

這段程序的本意是,啟動15個線程,線程向map中寫入20個整型值,然后輸出map。運行該程序,觀察結果,我們會發現,map中壓根就不止20個元素,這說明程序產生了線程安全問題。

我們都知道HashMap是非線程安全的,程序啟動了15個線程,他們共享了同一個map,15個線程都往map寫對象,這勢必引起線程安全問題。

我們有兩種方法解決這個問題:

  1. 將map的聲明放到run方法中,這樣map就成了方法內部變量,每個線程都有一份new HashMap(),無論多少個線程執行run方法,都不會有線程安全問題。這個方法也正如應用場景中提到的,如果有多處地方使用到map,傳值是個煩人的地方。

  2. 將HashMap換成Hashtable。用線程同步來解決問題,然而我們的程序只是想向一個map中寫入20個整型的KEY-VALUE而已,并不需要線程同步,同步勢必影響性能,得不償失。

  3. ThreadLocal提供另外一種解決方案,即在解決方案a上邊,將new HashMap()得到的實例變量,綁定到當前線程中。之后從任何地方,都可以通過ThreadLocal獲取到該變量。將程序中的注釋代碼恢復,再將 private final static Map map = new HashMap();注釋掉,運行程序,結果就是我們想要的。

實現原理

程序調用了get()方法,我們來看一下該方法的源碼:

 

  1. public T get() { 
  2.  
  3. Thread t = Thread.currentThread(); 
  4.  
  5. ThreadLocalMap map = getMap(t); 
  6.  
  7. if (map != null) { 
  8.  
  9. ThreadLocalMap.Entry e = map.getEntry(this); 
  10.  
  11. if (e != null
  12.  
  13. return (T)e.value; 
  14.  
  15.  
  16. return setInitialValue(); 
  17.  
  18.  
  19. getMap方法的源碼: 
  20.  
  21. ThreadLocalMap getMap(Thread t) { 
  22.  
  23. return t.threadLocals; 
  24.  

該方法返回的是當前線程中的ThreadLocalMap實例。閱讀Thread的源碼我們發現Thread中有如下變量聲明:

 

  1. /* ThreadLocal values pertaining to this thread. This map is maintained 
  2.  
  3. * by the ThreadLocal class. */ 
  4.  
  5. ThreadLocal.ThreadLocalMap threadLocals = null

我們暫時可以將ThreadLocalMap理解為一個類似Map的這么個類,之后再講解它。

get()方法的大致意思就是從當前線程中拿到ThreadLocalMap的實例threadLocals,如果threadLocals不為 空,那么就以當前ThreadLocal實例為KEY從threadLocals中拿到對應的VALUE。如果不為空,那么就調用 setInitialValue()方法初始化threadLocals,最終返回的是initialValue()方法的返回值。下面是 setInitialValue()方法的源碼

 

  1. private T setInitialValue() { 
  2.  
  3. T value = initialValue(); 
  4.  
  5. Thread t = Thread.currentThread(); 
  6.  
  7. ThreadLocalMap map = getMap(t); 
  8.  
  9. if (map != null
  10.  
  11. map.set(this, value); 
  12.  
  13. else 
  14.  
  15. createMap(t, value); 
  16.  
  17. return value; 
  18.  

我們看到map.set(this, value);這句代碼將ThreadLocalMap的實例作為KEY,將initialValue()的返回值作為VALUE,set到了threadLocals中。

程序在聲明ThreadLocal實例的時候覆寫了initialValue(),返回了VALUE,當然我們可以直接調用set(T t)方法來設置VALUE。下面是set(T t)方法的源碼:

 

  1. public void set(T value) { 
  2.  
  3. Thread t = Thread.currentThread(); 
  4.  
  5. ThreadLocalMap map = getMap(t); 
  6.  
  7. if (map != null
  8.  
  9. map.set(this, value); 
  10.  
  11. else 
  12.  
  13. createMap(t, value); 
  14.  

我們看到它比setInitialValue()方法就少了個return語句。這兩種方式都能達到初始化ThreadLocalMap實例的效果。

我們再來看一下ThreadLocal類的結構。

ThreadLocal類只有三個屬性,如下:

  1. /*ThreadLocal的hash值,map用它來存儲值*/ 

private final int threadLocalHashCode = nextHashCode();

/*改類能以原子的方式更新int值,這里主要是在產生新的ThreadLocal實例時用來產生一個新的hash值,map用該值來存儲對象*/

 

  1. private static AtomicInteger nextHashCode = 
  2. new AtomicInteger(); 

/*該變量標識每次產生新的ThreadLocal實例時,hash值的增量*/

private static final int HASH_INCREMENT = 0x61c88647;

剩下的就是一些方法。最關鍵的地方就是ThreadLocal定義了一個靜態內部類ThreadLocalMap。我們在下一章節再來分析這個類。 從ThreadLocal的類結構,我們可以看到,實際上問題的關鍵先生是ThreadLocalMap,ThreadLocal只是提供了管理的功能, 我們也可以說ThreadLocal只是代理了ThreadLocalMap而已。

ThreadLocalMap源碼分析

既然ThreadLocalMap實現了類似map的功能,那我們首先來看看它的set方法源碼:

 

  1. private void set(ThreadLocal key, Object value) { 
  2.  
  3. // We don’t use a fast path as with get() because it is at 
  4.  
  5. // least as common to use set() to create new entries as 
  6.  
  7. // it is to replace existing ones, in which case, a fast 
  8.  
  9. // path would fail more often than not. 
  10.  
  11. Entry[] tab = table; 
  12.  
  13. int len = tab.length; 
  14.  
  15. int i = key.threadLocalHashCode & (len-1); 
  16.  
  17. for (Entry e = tab[i]; 
  18.  
  19. e != null
  20.  
  21. e = tab[i = nextIndex(i, len)]) { 
  22.  
  23. ThreadLocal k = e.get(); 
  24.  
  25. if (k == key) { 
  26.  
  27. e.value = value; 
  28.  
  29. return
  30.  
  31.  
  32. if (k == null) { 
  33.  
  34. replaceStaleEntry(key, value, i); 
  35.  
  36. return
  37.  
  38.  
  39.  
  40. tab[i] = new Entry(key, value); 
  41.  
  42. int sz = ++size; 
  43.  
  44. if (!cleanSomeSlots(i, sz) && sz >= threshold) 
  45.  
  46. rehash(); 
  47.  

這個方法的主要功能就是講KEY-VALUE存儲到ThreadLocalMap中,這里至少我們看到KEY實際上是 key.threadLocalHashCode,ThreadLocalMap同樣維護著Entry數組,這個Entry我們在下一節會講解。這里涉及 到了Hash沖突的處理,這里并不會向HashMap一樣沖突了以鏈表的形式往后添加。如果對這個Hash沖突解決方案有興趣,可以再進一步研究源碼。

既然ThreadLocalMap也是用Entry來存儲對象,那我們來看看Entry類的聲明,Entry被定義在ThreadLocalMap的內部:

 

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

這里我們看到Entry集成了WeakReference類,泛型聲明了ThreadLocal,即每一個Entry對象都保留了對 ThreadLocal實例的弱引用,之所以這么干的原因是,線程在結束之后需要將ThreadLocal實例從map中remove調,以便回收內存空 間。

總結

首先,ThreadLocalMap并不是為了解決線程安全問題,而是提供了一種將實例綁定到當前線程的機制,類似于隔離的效果,實際上自己在方法 中new出來變量也能達到類似的效果。ThreadLocalMap跟線程安全基本不搭邊,綁定上去的實例也不是多線程公用的,而是每個線程new一份, 這個實例肯定不是共用的,如果共用了,那就會引發線程安全問題。ThreadLocalMap***的用處就是用來把實例變量共享成全局變量,在程序的任何 方法中都可以訪問到該實例變量而已。網上很多人說ThreadLocalMap是解決了線程安全問題,其實是望文生義,兩者不是同類問題。

責任編輯:王雪燕 來源: 并發編程網
相關推薦

2023-10-07 08:26:40

多線程數據傳遞數據共享

2021-05-06 08:55:24

ThreadLocal多線程多線程并發安全

2023-02-28 11:27:50

線程處理解決共享變量

2024-08-13 15:07:20

2023-08-02 08:54:58

Java弱引用鏈表

2022-05-11 07:36:12

Java線程安全

2022-03-17 08:55:43

本地線程變量共享全局變量

2021-09-13 15:17:52

FastThreadL源碼Java

2024-10-28 08:15:32

2023-05-29 07:17:48

內存溢出場景

2018-04-09 08:17:36

線程ThreadLocal數據

2022-04-02 08:14:02

JavaThreadLoca數據

2021-04-13 09:20:21

JavaUnsafejava8

2022-05-09 07:27:50

ThreadLocaJava

2020-07-28 08:59:22

JavahreadLocal面試

2009-09-29 17:11:23

Hibernate T

2011-07-14 13:50:09

ThreadLocal

2016-09-18 16:58:09

JavaProperties

2019-11-27 10:05:00

LombokJava編程

2021-12-31 18:24:45

ThreadLocal數據庫對象
點贊
收藏

51CTO技術棧公眾號

主站蜘蛛池模板: 五月综合久久 | 欧美久久久久久久久 | www.中文字幕av | 国产中文字幕亚洲 | 久久免费精品 | 亚洲国产一区二区三区在线观看 | 亚洲毛片在线观看 | 青青久久| 国产精品无码久久久久 | 精品一区久久 | 亚洲人精品午夜 | 国产精品精品久久久 | 久久久久免费精品国产 | 日韩综合一区 | 中文字幕福利视频 | 婷婷色成人 | 午夜久久久 | 男女精品网站 | 久久国产精品久久久久久 | 久久里面有精品 | 国产一级片一区二区 | 国内在线视频 | 色婷婷av一区二区三区软件 | 美女久久久久久久 | 高清免费av | 欧美特级黄色 | 欧美aaaaaaaaaa| 韩国毛片一区二区三区 | 狠狠综合久久av一区二区小说 | 午夜国产羞羞视频免费网站 | 狠狠撸在线视频 | 秋霞在线一区 | 久久精品99久久 | 国产在线视频一区二区 | www.中文字幕.com | 日韩激情视频一区 | 精品日韩 | 在线欧美亚洲 | 精品视频一区二区三区在线观看 | 99久久婷婷国产精品综合 | 国产1区2区3区 |