国产片侵犯亲女视频播放_亚洲精品二区_在线免费国产视频_欧美精品一区二区三区在线_少妇久久久_在线观看av不卡

服務器之家:專注于服務器技術及軟件下載分享
分類導航

PHP教程|ASP.NET教程|Java教程|ASP教程|編程技術|正則表達式|C/C++|IOS|C#|Swift|Android|VB|R語言|JavaScript|易語言|vb.net|

服務器之家 - 編程語言 - Java教程 - ThreadLocal常用方法、使用場景及注意事項說明

ThreadLocal常用方法、使用場景及注意事項說明

2022-02-15 16:08一碼事 Java教程

這篇文章主要介紹了ThreadLocal常用方法、使用場景及注意事項說明,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教

1. ThreadLocal詳解

JDK1.2版本起,Java就提供了java.lang.ThreadLocal,ThreadLocal為每個使用線程都提供獨立的變量副本,可以做到線程間的數據隔離,每個線程都可以訪問各自內部的副本變量。

線程上下文ThreadLocal又稱為"線程保險箱",ThreadLocal能夠將指定的變量和當前線程進行綁定,線程之間彼此隔離,持有不同的對象實例,從而避免了數據資源的競爭。

 

2. ThreadLocal的使用場景

  • 在進行對象跨層傳遞的時候,可以考慮ThreadLocal,避免方法多次傳遞,打破層次間的約束。
  • 線程間數據隔離。
  • 進行事務操作,用于儲存線程事務信息。

注意:

ThreadLocal并不是解決多線程下共享資源的一種技術,一般情況下,每一個線程的ThreadLocal存儲的都是一個全新的對象(通過new關鍵字創建),如果多線程的ThreadLocal存儲了一個對象引用,那么就會面臨資源競爭,數據不一致等并發問題。

 

3.常用方法源碼解析

3.1 initialValue方法

protected T initialValue() {
      return null;
}

此方法為ThreadLocal保存的數據類型指定的一個初始化值,在ThreadLocal中默認返回null。但可以重寫initialValue()方法進行數據初始化。

如果使用的是Java8提供的Supplier函數接口更加簡化:

// withInitial()實際是創建了一個ThreadLocal的子類SuppliedThreadLocal,重寫initialValue()
ThreadLocal<Object> threadLocal = ThreadLocal.withInitial(Object::new);

3.2 set(T value)方法

主要存儲指定數據。

public void set(T value) {
  // 獲取當前線程Thread.currentThread() 
  Thread t = Thread.currentThread();
  // 根據當前線程獲取與之關聯的ThreadLocalMap數據結構
  ThreadLocalMap map = getMap(t);
  if (map != null)
      // 核心方法。set 遍歷整個Entry的過程,后面有詳解
      map.set(this, value);
  else {
      // 調用createMap(),創建ThreadLocalMap,key為當前ThreadLocal實例,存入數據為當前value。
      // ThreadLocal會創建一個默認長度為16Entry節點,并將k-v放入i位置(i位置計算方式和hashmap相似,
      // 當前線程的hashCode&(entry默認長度-1)),并設置閾值(默認為0)為Entry默認長度的2/3。
      createMap(t, value);
  }
}
// set 遍歷整個Entry的過程
private void set(ThreadLocal<?> key, Object value) {
  // 獲取所有的Entry
  Entry[] tab = table;
  int len = tab.length;
  // 根據ThreadLocal對象,計算角標位置
  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;
      }
// 如果ThreadLocal為null,直接驅出并使用新數據(Value)占居原來位置,
// 這個過程主要是防止內存泄漏。
      if (k == null) {
          // 驅除ThreadLocal為null的Entry,并放入Value,這也是內存泄漏的重點地區
          replaceStaleEntry(key, value, i);
          return;
      }
  }
// entry都為null,創建新的entry,已ThreadLocal為key,將存放數據為Value。
  tab[i] = new Entry(key, value);
  int sz = ++size;
  // ThreadLoaclMapde的當前數據元素的個數和閾值比較,再次進行key為null的清理工作。
  if (!cleanSomeSlots(i, sz) && sz >= threshold)
      // 整理Entry,當Entry中的ThreadLocal對象為null時,通過重新計算角標位來清理
      // 以前ThreadLocal。如果Entry數量大于3/4容量進行擴容
      rehash();
}

3.3 get方法

get()用于返回當前線程ThreadLocal中數據備份,當前線程的數據都存在一個ThreadLocalMap的數據結構中。

public T get() {
  Thread t = Thread.currentThread();
  // 獲得ThreadLocalMap對象map,ThreadLocalMap是和當前Thread關聯的,
  ThreadLocalMap map = getMap(t);
  if (map != null) {
      // 存入ThreadLocal中的數據實際上是存儲在ThreadLocalMap的Entry中。
      // 而此Entry是放在一個Entry數組里面的。
      // 獲取當前ThreadLocal對應的entry
      ThreadLocalMap.Entry e = map.getEntry(this);
      if (e != null) {
          // 直接返回當前數據 
          T result = (T)e.value;
          return result;
      }
  }
  // ThreadLocalMap未初始化,首先初始化
  return setInitialValue();
}
// ThreadLocal的setInitialValue方法源碼
private T setInitialValue() {
  // 為ThreadLocalMap指定Value的初始化值
  T value = initialValue();
  Thread t = Thread.currentThread();
  // 根據本地線程Thread獲取ThreadLocalMap,一下方法與Set方法相同。
  ThreadLocalMap map = getMap(t);
  if (map != null)
      // 如果map存在,直接調用set()方法進行賦值。
      map.set(this, value);
  else
      // map==null;創建ThreadLocalMap對象,并將Thread和value關聯起來
      createMap(t, value);
  return value;
}

3.4 小結

  • initialValue():初始化ThreadLocal中的value屬性值。
  • set():獲取當前線程,根據當前線程從ThreadLocals中獲取ThreadLocalMap數據結構,
    • 如果ThreadLocalmap的數據結構沒創建,則創建ThreadLocalMap,key為當前ThreadLocal實例,存入數據為當前value。ThreadLocal會創建一個默認長度為16Entry節點,并將k-v放入i位置(i位置計算方式和hashmap相似,當前線程的hashCode&(entry默認長度-1)),并設置閾值(默認為0)為Entry默認長度的2/3。
    • 如果ThreadLocalMap存在。就會遍歷整個Map中的Entry節點,如果entry中的key和本線程ThreadLocal相同,將數據(value)直接覆蓋,并返回。如果ThreadLoca為null,驅除ThreadLocal為null的Entry,并放入Value,這也是內存泄漏的重點地區。
  • get()
  • get()方法比較簡單。就是根據Thread獲取ThreadLocalMap。通過ThreadLocal來獲得數據value。注意的是:如果ThreadLocalMap沒有創建,直接進入創建過程。初始化ThreadLocalMap。并直接調用和set方法一樣的方法。

3.4 ThreadLocalMap數據結構

set()還是get()方法都是避免不了和ThreadLocalMap和Entry打交道。ThreadLocalMap是一個類似于HashMap的一個數據結構(沒有鏈表),僅僅用于存放線程存放在ThreadLocal中的數據備份,ThreadLocalMap的所有方法對外部都是不可見的。

ThreadLocalMap中用于存儲數據的Entry,它是一個WeakReference類型的子類,之所以設計成WeakReference是為了能夠是JVM發生gc,能夠自動回收,防止內存溢出現象。

 

4. ThreadLocal的副作用

4.1 ThreadLocal引起臟數據

線程復用會產生臟數據。

由于結程池會重用 Thread 對象 ,那么與 Thread 綁定的類的靜態屬性 ThreadLocal 變量也會被重用。如果在實現的線程 run()方法體中不顯式地調用 remove() 清理與線程相關的ThreadLocal 信息,那么如果下一個線程不調用set()設置初始值,就可能 get()到重用的線程信息,包括 ThreadLocal 所關聯的線程對象的 value 值。

// java.lang.Thread#threadLocals
/* ThreadLocal values pertaining to this thread. This map is maintained
   * by the ThreadLocal class. */
ThreadLocal.ThreadLocalMap threadLocals = null;

4.2 ThreadLocal引起的內存泄漏

在上面提到ThreadLocalMap中存放的Entry是WeakReference的子類。所以在JVM觸發GC(young gc,Full GC)時,都會導致Entry的回收

在get數據的時候,增加檢查,清除已經被回收器回收的Entry(WeakReference可以自動回收)

private Entry getEntryAfterMiss(ThreadLocal<?> key, int i, Entry e) {
  ThreadLocal<?> k = e.get();
...
  if (k == null)
      // 清除 key 是 null 的Entry
      expungeStaleEntry(i);
...
return null;
}
private boolean cleanSomeSlots(int i, int n) {
  boolean removed = false;
  Entry[] tab = table;
  int len = tab.length;
  do {
      i = nextIndex(i, len);
      Entry e = tab[i];
      if (e != null && e.get() == null) {
          n = len;
          removed = true;
          // 清除key==null 的Entry
          i = expungeStaleEntry(i);
      }
  } while ( (n >>>= 1) != 0);
  return removed;
}

set數據時增加檢查,刪除已經被垃圾回收器清理的Entry,并將其移除

private boolean cleanSomeSlots(int i, int n) {
  boolean removed = false;
  Entry[] tab = table;
  int len = tab.length;
  do {
      i = nextIndex(i, len);
      Entry e = tab[i];
      if (e != null && e.get() == null) {
          n = len;
          removed = true;
          // 清除key==null 的Entry
          i = expungeStaleEntry(i);
      }
  } while ( (n >>>= 1) != 0);
  return removed;
}

基于上面三點:ThreadLocal在一定程度上保證不會發生內存泄漏。但是Thread類中有ThreadlocalMap的引用,導致對象的可達性,故不能回收。

ThreadLocal被置為null清除了。但是通過ThreadLocalMap還是被Thread類引用。導致該數據是可達的。所以內存得不到釋放,除非當前線程結束,Thread引用就會被垃圾回收器回收。如圖所示

ThreadLocal常用方法、使用場景及注意事項說明

ThreadLocal常用方法、使用場景及注意事項說明5

 

5. ThreadLocal內存泄漏解決方案及remove方法源碼解析

解決ThreadLocal內存泄漏的常用方法是:在使用完ThreadLocal之后,及時remove掉。

public void remove() {
  // 根據當前線程,獲取ThreadLocalMap
  ThreadLocalMap m = getMap(Thread.currentThread());
  if (m != null)
      // map不為null,執行remove操作
      m.remove(this);
}
// ThreadLocal 的remove()
private void remove(ThreadLocal<?> key) {
  // 獲取存放key-value的數組。
  Entry[] tab = table;
  int len = tab.length;
  // 根據ThreadLocal的HashCode確定唯一的角標
  int i = key.threadLocalHashCode & (len-1);
  for (Entry e = tab[i];e != null;e = tab[i = nextIndex(i, len)]) {
      if (e.get() == key) {
          // 如果和本ThreadLocal相同。將引用置null。
          e.clear();
          // 實行Enty和Entry.value置null。源碼中 tab[staleSlot].value = null; tab[staleSlot] = null;
          expungeStaleEntry(i);
          return;
      }
  }
}

以上為個人經驗,希望能給大家一個參考,也希望大家多多支持服務器之家。

原文鏈接:https://blog.csdn.net/qq_42742861/article/details/90649837

延伸 · 閱讀

精彩推薦
Weibo Article 1 Weibo Article 2 Weibo Article 3 Weibo Article 4 Weibo Article 5 Weibo Article 6 Weibo Article 7 Weibo Article 8 Weibo Article 9 Weibo Article 10 Weibo Article 11 Weibo Article 12 Weibo Article 13 Weibo Article 14 Weibo Article 15 Weibo Article 16 Weibo Article 17 Weibo Article 18 Weibo Article 19 Weibo Article 20 Weibo Article 21 Weibo Article 22 Weibo Article 23 Weibo Article 24 Weibo Article 25 Weibo Article 26 Weibo Article 27 Weibo Article 28 Weibo Article 29 Weibo Article 30 Weibo Article 31 Weibo Article 32 Weibo Article 33 Weibo Article 34 Weibo Article 35 Weibo Article 36 Weibo Article 37 Weibo Article 38 Weibo Article 39 Weibo Article 40
主站蜘蛛池模板: 久草久 | 二区视频 | 午夜国产 | 欧美日韩国产中文 | 精品久久精品久久 | 91精品国产91久久久久久吃药 | 免费的黄色一级片 | 国产黄视频在线 | 日本久久精品 | 亚洲精品无 | 欧美日韩国产一区 | 久久久久国产 | av人人看 | 亚洲精品乱码久久久久膏 | 国产一区二区资源 | 中文字幕在线三区 | 中文学幕专区 | 在线观看欧美日韩 | 免费的av网站 | 中文字幕在线免费看 | 欧美在线网站 | 欧美成人精品一区二区三区在线看 | 国产成人a亚洲精品 | 久久综合av | 91精品久久久久久 | 久久亚洲国产 | 狠狠干五月天 | 中文字幕 亚洲一区 | 久久久亚洲国产美女国产盗摄 | 懂色av中文字幕一区二区三区 | 国产精品污www一区二区三区 | 国产精品永久免费自在线观看 | 蜜桃视频网站入口 | 一区二区在线 | 欧美精品成人一区二区三区四区 | 国产成人综合一区二区三区 | 久久久九九 | 一区二区三区日韩在线 | 国产 日韩 欧美 在线 | 日韩中文字幕在线免费观看 | 一级黄色大片 |