首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> Java 程式設計 >> Java 相關知識

Java 多線程同步問題探究(五):ThreadLocal 解決並發安全

     編輯:Java 相關知識

ThreadLocal:以空間換時間的並發方案

前文介紹了多線程搶佔共享資源的問題。無論同步還是重入鎖,都無法從根本解決資源緊缺,只是透過規則約束線程行為,而非真正滿足資源需求。

JDK 1.2 引入 java.lang.ThreadLocal,提供全新思路解決並發問題。名稱易誤解為「本地線程」,實際是「線程中的本地變量」。源於 Thread Local Storage(線程本地存儲)技術,不只 Java,C++、C#、.NET、Python、Ruby、Perl 等平台皆已實現。

核心機制

使用 ThreadLocal 維護變量時,為每個使用該變量的線程提供獨立變量副本。從根本解決資源數量問題,使每線程持有相對獨立資源。線程工作時無需糾結同步,性能大幅提升。代價是更多空間消耗——典型的「以空間換時間」方案。

關鍵方法

  • get():返回當前線程副本中的值
  • set():設定當前線程副本中的值
  • remove():移除當前線程的值
  • initialValue():返回當前線程的「初始值」(需覆寫)

常見錯誤與正確用法

錯誤範例:在主線程 set,子線程 get

public class ThreadDemo3 extends Thread {
  private ThreadLocal<Student> stuLocal = new ThreadLocal<Student>();
  public ThreadDemo3(Student stu) { stuLocal.set(stu); }
  // ... run() 中呼叫 stuLocal.get() 會得到 null
}

原因:構造器在主線程執行,set() 將值放入主線程的 ThreadLocalMap。子線程啟動後調用 get(),從自身 ThreadLocalMap 取值,發現無對應 entry,執行 initialValue() 返回 null。

ThreadLocal 內部原理

  • 每個 Thread 物件有 threadLocals 引用指向 ThreadLocalMap
  • ThreadLocalMap 非 java.util.Map 實作,用 WeakReference 實現鍵值對應,鍵為 ThreadLocal,值為 Object
  • set():從當前線程取 ThreadLocalMap,有則以自己為鍵存值;無則建立 Map 再存
  • get():從當前線程取 Map,有則以自己為鍵取值;無則建立 Map 並存入 initialValue() 結果

正確用法:覆寫 initialValue()

private ThreadLocal<Student> stuLocal = new ThreadLocal<Student>() {
  @Override
  protected Student initialValue() {
    return new Student();
  }
};

每個線程首次 get() 時自動獲得獨立的 Student 實例。

一個 ThreadLocal 對應多線程

public class ThreadDemo3 implements Runnable {
  private ThreadLocal<Student> stuLocal = new ThreadLocal<Student>() { ... };
  public static void main(String[] args) {
    ThreadDemo3 td3 = new ThreadDemo3();
    new Thread(td3).start();
    new Thread(td3).start();
    new Thread(td3).start();
  }
}

多線程共用同一 Runnable 實例,故共用同一 ThreadLocal 物件。但每線程有自己的 ThreadLocalMap,鍵為同一 ThreadLocal,值為各自獨立實例。本質無差異:真正封裝變量的是 ThreadLocalMap,ThreadLocal 只是中介。

使用要點總結

  1. 總是覆寫 initialValue() 方法
  2. 牢記 set()/get() 操作的是當前活動線程的 ThreadLocalMap,務必認清當前線程
  3. 適當使用泛型減少類型轉換問題

效能對比

程式執行僅需 5 秒;採用同步需 15 秒。ThreadLocal 讓每線程擁有獨立資源,無需互相謙讓,提高並發性並保證線程安全。

Struts 2.x、Spring 等流行框架也採用 ThreadLocal 解決並發問題。

適用場景與限制

ThreadLocal 非萬能藥。若實際情況不允許為每線程分配本地資源副本,同步仍有重要意義。它是將多線程數據隔離、避免同步性能問題的「以空間換時間」方案。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved