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

Java 多線程核心總結:創建終止、互斥同步、線程通信、ThreadLocal 詳解

     編輯:Java 相關知識

多線程是 Java 並發編程的基石。本文系統梳理線程創建與終止、互斥同步、線程通信、ThreadLocal 本地變量四大核心模組,結合源碼分析與實戰案例,構建完整的多線程知識體系。


一、線程創建與終止

1. 兩種創建方式

核心關係:Thread 實現了 Runnable 接口,start() 啟動線程,JVM 調用 run() 方法。

方式代碼範例優缺點
繼承 Thread 重寫 run()
class MyThread extends Thread {
    @Override public void run() { ... }
}
new MyThread().start();
簡單,但無法繼承其他類
實現 Runnable 重寫 run()
class MyTask implements Runnable {
    @Override public void run() { ... }
}
new Thread(new MyTask()).start();
推薦:解耦任務與線程,支持線程池、Lambda 表達式

2. 線程終止:interrupt 機制

棄用 stop()/suspend():粗暴釋放鎖導致數據不一致、死鎖風險。

標準終止模式:interrupt() 設置中斷標誌 → 線程檢查標誌或阻塞方法拋出 InterruptedException → 捕獲異常退出 run()。

class SafeThread extends Thread {
    @Override
    public void run() {
        try {
            while (!isInterrupted()) {  // 檢查中斷標誌
                Thread.sleep(1000);     // 阻塞方法可感知中斷
                System.out.println(getName() + " running");
            }
        } catch (InterruptedException e) {
            System.out.println(getName() + " 被中斷,安全退出");
        }
    }
}

public class InterruptDemo {
    public static void main(String[] args) throws Exception {
        SafeThread t = new SafeThread();
        t.start();
        Thread.sleep(5000);
        t.interrupt();  // 發送中斷信號
    }
}

3. 三個關鍵方法區別

  • interrupt():設置中斷標誌為 true
  • isInterrupted():不清除標誌,返回當前狀態
  • interrupted():靜態方法,檢查並清除當前線程標誌

4. 線程狀態轉換圖

NEW → RUNNABLE → RUNNING → TERMINATED
運行態可轉:BLOCKED(鎖等待)、WAITING(無限等待)、TIMED_WAITING(計時等待)


二、線程互斥:synchronized 深度解析

1. 核心概念

  • 對象鎖:每個對象一把鎖,synchronized 修飾實例方法/代碼塊,鎖住 this
  • 全局鎖(類鎖):synchronized 修飾靜態方法/代碼塊,鎖住 Class 對象

2. 兩種同步寫法

// 同步方法(隱式鎖 this)
public synchronized void output(String name) { ... }

// 同步代碼塊(顯式鎖)
public void output(String name) {
    synchronized (this) { ... }
}

3. synchronized 四大用法規律

用法場景效果
1 線程A訪問obj同步方法 線程B訪問obj同步方法/塊 → 阻塞
2 線程A訪問obj同步方法1 線程B訪問obj同步方法2 → 阻塞(同一把鎖)
3 線程A訪問obj同步方法 線程B訪問obj非同步方法 → 不阻塞
4 線程A訪問靜態同步方法 線程B訪問同類靜態同步方法/塊 → 阻塞(類鎖)

三、線程通信:wait/notify 機制

1. 核心方法(Object 類)

  • wait():釋放鎖,進入等待集合(WAITING)
  • notify():喚醒單個等待線程
  • notifyAll():喚醒所有等待線程
必須在 synchronized 方法/塊中調用,否則拋 IllegalMonitorStateException。

2. wait vs yield

方法狀態轉換鎖行為用途
wait()RUNNING → WAITING釋放鎖線程協作
yield()RUNNING → RUNNABLE保持鎖測試讓權(不推薦生產)

3. 經典案例:主線程與子線程交替打印

class AlternatePrinter {
    private volatile boolean childTurn = false;  // volatile 保證可見性

    public synchronized void childPrint() throws InterruptedException {
        while (!childTurn) {
            System.out.println(Thread.currentThread().getName() + " 等待中...");
            wait();  // 釋放鎖等待
        }
        for (int i = 1; i <= 3; i++) {
            System.out.println("子線程打印: " + i);
        }
        childTurn = false;
        notify();  // 喚醒主線程
    }

    public synchronized void mainPrint() throws InterruptedException {
        while (childTurn) {
            System.out.println(Thread.currentThread().getName() + " 等待中...");
            wait();
        }
        for (int i = 1; i <= 3; i++) {
            System.out.println("主線程打印: " + i);
        }
        childTurn = true;
        notify();  // 喚醒子線程
    }
}

四、ThreadLocal:線程本地變量

1. 核心價值

為每個線程提供獨立的變量副本,線程間互不干擾,避免同步開銷。適用於:數據庫連接、Session 管理、事務上下文傳遞。

2. 源碼核心結構

  • Thread 類持有 ThreadLocalMap threadLocals
  • ThreadLocalMap:定製化 HashMap,Key 為 ThreadLocal 對象(弱引用),Value 為線程變量值
  • 線程結束,Map 隨之消亡,Entry 弱引用特性配合 GC 自動清理

3. 關鍵流程

// set 流程
public void set(T value) {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) map.set(this, value);
    else createMap(t, value);
}

// get 流程
public T get() {
    Thread t = Thread.currentThread();
    ThreadLocalMap map = getMap(t);
    if (map != null) {
        Entry e = map.getEntry(this);
        if (e != null) return (T)e.value;
    }
    return setInitialValue();  // 惰性初始化
}

4. 實戰案例:線程隔離的 Passenger 數據

class Passenger {
    private String name; private int age;
    // ThreadLocal 存儲線程獨有實例
    private static ThreadLocal tl = new ThreadLocal<>();

    public static Passenger getInstance() {
        Passenger p = tl.get();
        if (p == null) { p = new Passenger(); tl.set(p); }
        return p;
    }
    // getter/setter...
}

// 業務模塊無需傳參,直接獲取當前線程數據
class ModuleA { void getData() { Passenger.getInstance().getName(); } }
class ModuleB { void getData() { Passenger.getInstance().getAge(); } }

// 測試:3個線程各自寫入、讀取互不干擾
for (int i = 0; i < 3; i++) {
    new Thread(() -> {
        Passenger.getInstance().setName(randomName());
        Passenger.getInstance().setAge(randomAge());
        new ModuleA().getData();
        new ModuleB().getData();
    }).start();
}

5. 線程間共享變量 vs ThreadLocal

  • 共享變量:需 volatile + synchronized 保證原子性、可見性
  • ThreadLocal:空間換時間,無鎖化,但需注意內存洩漏(手動 remove())

總結

  • 創建/終止:優先 Runnable + 線程池,interrupt 協作式終止
  • 互斥:synchronized 鎖對象/類,掌握四大用法規律
  • 通信:wait/notify 依賴對象鎖,volatile 保證標誌可見性
  • ThreadLocal:線程隔離利器,弱引用 Key 防洩漏,用完必 remove

多線程編程的本質是狀態管理與協作協議。深入理解 JVM 內存模型、鎖優化、線程池原理,才能駕馭高併發系統設計。

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