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

Java 多線程核心篇:建立/終止、互斥鎖、線程通信、ThreadLocal 完整實戰

     編輯:Java 綜合教學

一、線程建立與終止

1. 兩種建立方式

  • 繼承 Thread:重寫 run(),調用 start() 啟動。
  • 實作 Runnable:實作 run(),包裝於 new Thread(runnable).start()。

本質:Thread 實作 Runnable,start() 觸發 JVM 調度,最終執行 Runnable.run()。

2. 正確終止線程

  • 避免使用:stop() (釋放鎖不安全)、suspend() (易死鎖)。
  • 推薦:interrupt() 設置中斷標記;阻塞方法 (sleep/wait) 會拋出 InterruptedException,捕獲後正常退出。
while (!Thread.currentThread().isInterrupted()) {
    try { Thread.sleep(1000); } catch (InterruptedException e) { break; }
}

3. 關鍵方法差異

  • interrupt():設置當前線程中斷標記為 true
  • isInterrupted():檢測中斷標記 (不清除)
  • Thread.interrupted():檢測當前線程中斷標記並清除

二、線程互斥 (同步)

1. 對象鎖 (實例鎖)

每個對象擁有一把鎖。synchronized 修飾實例方法/代碼塊 → 鎖住 this。

2. 全域鎖 (類鎖)

synchronized 修飾靜態方法/代碼塊 → 鎖住 ClassName.class。

3. 兩種同步寫法

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

// 同步代碼塊 (更精確控制範圍)
public void output(String name) {
    synchronized(this) { ... }
}

4. synchronized 四大使用規則

  1. 線程進入對象的同步方法/塊 → 其他線程訪問該對象的所有同步方法/塊阻塞。
  2. 同上,即使訪問不同的同步方法/塊也會阻塞 (同一把鎖)。
  3. 非同步方法/塊不受影響,可並發訪問。
  4. 靜態同步鎖住 Class 物件,與實例鎖互不干擾。

三、線程通信 (wait/notify)

核心機制

  • wait():釋放鎖,進入等待集合 (WAITING)
  • notify():喚醒一個等待線程
  • notifyAll():喚醒所有等待線程

⚠️ 必須在 synchronized 方法/塊中調用,且調用對象必須為鎖對象。

wait() vs yield()

wait()yield()
狀態轉換RUNNING → WAITING (阻塞)RUNNING → RUNNABLE (就緒)
釋放鎖是否
用途線程協作僅提示調度器,測試用

經典範例:主/子線程交替輸出

public class WaitNotifyDemo {
    static volatile boolean childTurn = false; // volatile 保證可見性

    public static void main(String[] args) {
        Object lock = new Object();
        Thread child = new Thread(() -> {
            for (int i = 0; i < 2; i++) synchronized (lock) {
                while (!childTurn) { lock.wait(); }
                for (int j = 1; j <= 3; j++) System.out.println("Child: " + j);
                childTurn = false; lock.notify();
            }
        });
        child.start();
        for (int i = 0; i < 2; i++) synchronized (lock) {
            while (childTurn) { lock.wait(); }
            for (int j = 1; j <= 3; j++) System.out.println("Main: " + j);
            childTurn = true; lock.notify();
        }
    }
}

四、ThreadLocal:線程專屬變數

原理

  • 每個 Thread 內部持有 ThreadLocalMap (key=ThreadLocal, value=專屬值)
  • set() 存入當前線程 Map;get() 從當前線程 Map 取值
  • Key 為弱引用,線程結束後 Entry 可被 GC 回收

應用場景:線程隔離共享物件

public class ThreadLocalDemo {
    static ThreadLocal holder = new ThreadLocal<>();

    static class Passenger { String name; int age; /* getter/setter */ }

    public static void main(String[] args) {
        for (int i = 0; i < 3; i++) new Thread(() -> {
            Passenger p = new Passenger();
            p.setName(randomName()); p.setAge(randomAge());
            holder.set(p); // 綁定當前線程
            new ModuleA().print();
            new ModuleB().print();
        }).start();
    }

    static class ModuleA { void print() { System.out.println("A: " + holder.get()); } }
    static class ModuleB { void print() { System.out.println("B: " + holder.get()); } }
}

多線程共享變數 (需同步)

static volatile int count = 100;
static class Dec { synchronized void dec() { System.out.println(Thread.currentThread().getName() + " dec -> " + --count); } }
static class Inc { synchronized void inc() { System.out.println(Thread.currentThread().getName() + " inc -> " + (count += 2)); } }
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved