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

Java 併發包 API 實戰(二):傳統線程技術回顧、定時器、同步通信與 ThreadLocal

     編輯:Java 綜合教學

1. 傳統線程技術回顧

繼承 Thread vs 實作 Runnable

  • Runnable 更符合 OO:任務與執行分離,支援同一任務多線程執行 (資料共享)
  • Thread.run() 源碼:若 target != null 則呼叫 target.run(),否則空實作
  • 若子類重寫 run() 且傳入 Runnable,**子類 run() 生效**,Runnable 被忽略

多線程一定提升效率嗎?

**不一定**。單核 CPU 下多線程因上下文切換反而降低效率。多線程下載「快」是因為佔用更多伺服器頻寬,而非本機 CPU 變快。

2. 傳統定時器

Timer / TimerTask (JDK 1.3+)

Timer timer = new Timer();
timer.schedule(new TimerTask() {
    public void run() { System.out.println("執行任務"); }
}, 1000, 2000); // 延遲 1s,每 2s 執行

// 固定速率
timer.scheduleAtFixedRate(task, date, period);

缺點:單執行緒串行執行、異常導致後續任務停止、無彈性排程。生產環境建議用 Quartz** 或 **ScheduledExecutorService**。

3. 傳統線程互斥

核心原則

  1. 多線程 (≥2) 訪問共享資源
  2. **必須使用同一把鎖**
// 錯誤示範:兩個 synchronized 但鎖不同
public void print1() { synchronized(MyClass.class) { ... } }
public void print2() { synchronized(this) { ... } } // 無互斥效果

靜態方法鎖 = Class 物件,實例方法鎖 = this。

4. 傳統線程同步通信 (生產者消費者)

設計原則:同步邏輯封裝在資源類內部

而非寫在線程類中,這樣資源類天然線程安全,任意線程均可安全調用。

經典範例:子線程循環 10 → 主線程循環 100 → 交替 50 輪

class Output {
    private boolean flag = true; // true: 主線程, false: 子線程
    
    public synchronized void sub(int i) {
        while (!flag) { wait(); } // while 防偽喚醒
        for (int j=0; j<10; j++) System.out.println(i + "子線程" + j);
        flag = true; notify();
    }
    public synchronized void main(int i) {
        while (flag) { wait(); }
        for (int j=0; j<100; j++) System.out.println(i + "主線程" + j);
        flag = false; notify();
    }
}

5. 線程範圍內共享變量

場景

同一線程內多模組 (A/B) 需共享數據,但不同線程間數據隔離 (如事務上下文、資料庫連線)。

手工 Map 實現

static Map datas = new HashMap<>();
// 存: datas.put(Thread.currentThread(), value);
// 取: datas.get(Thread.currentThread());

6. ThreadLocal:標準解決方案

核心原理

  • 每個 Thread 內部持有 ThreadLocalMap
  • ThreadLocal 作為 Key,值存入當前線程的 Map
  • get()/set() 自動綁定當前線程

使用模式

// 1. 定義 (通常 static)
static ThreadLocal connLocal = new ThreadLocal<>();

// 2. 綁定 (如 Filter/Interceptor 開啟事務時)
connLocal.set(dataSource.getConnection());

// 3. 任意處取用 (同一線程)
Connection conn = connLocal.get();

// 4. 移除 (防洩漏,如請求結束)
connLocal.remove();

進階:InheritableThreadLocal

父線程建立子線程時自動複製值,適用於追蹤 ID 透傳。

記憶體洩漏風險與防範

  • ThreadLocalMap Key 為弱引用,Value 強引用
  • 若 ThreadLocal 實例回收但線程存活 (如線程池),Value 無法 GC
  • **必須在任務結束時呼叫 remove()**
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved