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

Java 多線程實戰教學(一):生命週期、建立方式與狀態轉換

     編輯:Java 相關知識

前言

多線程是 Java 並發編程的基石,亦是面試必考、實戰核心。本系列從線程生命週期、三種建立方式、狀態轉換、啟動與終止機制切入,配合原始碼級分析與實戰範例,建立扎實的多線程心智模型。

一、線程生命週期與五大基本狀態

Java 線程狀態轉換圖

狀態英文觸發條件說明
新建NEWnew Thread()物件建立,未啟動
可執行RUNNABLEstart()就緒佇列等待 CPU 調度(含 Running)
運行RUNNING獲得 CPU 時間片實際執行 run() 邏輯
阻塞BLOCKED見下文三類暫時放棄 CPU,需外部條件喚醒
死亡TERMINATEDrun() 結束/異常生命週期結束

阻塞的三種細分

  1. 等待阻塞:wait() → 需 notify()/notifyAll() 喚醒
  2. 同步阻塞:搶佔 synchronized 鎖失敗 → 進入 Entry Set 等待
  3. 其他阻塞:sleep()、join()、I/O 操作 → 時間到/完成自動轉 RUNNABLE

二、三種建立線程的標準姿勢

1. 繼承 Thread 類別

class MyThread extends Thread {
    @Override public void run() {
        for (int i = 0; i < 100; i++) {
            System.out.println(getName() + " " + i);
        }
    }
}

// 使用
Thread t1 = new MyThread();
t1.start(); // 必須呼叫 start(),而非 run()!

關鍵:start() 啟動新線程並呼叫 run();直接呼叫 run() 只在主線程順序執行。

2. 實作 Runnable 介面(推薦)

class MyTask implements Runnable {
    @Override public void run() { /* 任務邏輯 */ }
}

// 使用
Runnable task = new MyTask();
new Thread(task).start();
new Thread(task).start(); // 同一任務實例可被多線程共享

優勢:任務與線程分離、支援共享實例、避免單一繼承限制、適合線程池提交。

3. Callable + FutureTask(需回傳值/拋異常)

class MyCallable implements Callable<Integer> {
    @Override public Integer call() {
        int sum = 0;
        for (int i = 0; i < 100; i++) sum += i;
        return sum; // 有回傳值
    }
}

// 使用
FutureTask<Integer> ft = new FutureTask<>(new MyCallable());
new Thread(ft).start();

Integer result = ft.get(); // 阻塞等待結果
System.out.println("Sum = " + result); // 4950

FutureTask 同時實作 Runnable 與 Future,兼具「可被線程執行」與「可獲取結果」雙重特性。

Thread 與 Runnable 關係圖解


Thread 實作了 Runnable
  │
  ├─ run() { if (target != null) target.run(); } // 委派給 target
  │
  └─ new Thread(runnable) → target = runnable

若同時繼承 Thread 並傳入 Runnable,子類別覆寫的 run() 會被執行(多態),Runnable 的 run() 被忽略。

三、線程啟動、運行、終止狀態轉換

轉換觸發
RUNNABLE → RUNNING獲得 CPU 時間片
RUNNING → RUNNABLEyield() / 時間片用盡 / 失去 CPU
RUNNING → TERMINATEDrun() 正常結束 / 未捕獲異常
RUNNING → BLOCKEDsleep()/wait()/join()/I/O/鎖競爭
BLOCKED → RUNNABLE喚醒/超時/I/O 完成/獲得鎖

yield() 陷阱

Thread.yield(); // 僅「建議」讓出 CPU,調度器可能立即再次選中同一線程

不可依賴 yield() 實現同步邏輯。

四、優雅終止線程模式

標誌位模式(標準做法)

class ControlledTask implements Runnable {
    private volatile boolean stop = false; // volatile 保證可見性
    
    @Override public void run() {
        while (!stop && !Thread.currentThread().isInterrupted()) {
            // 執行任務邏輯
        }
    }
    
    public void requestStop() { stop = true; }
}

// 使用
ControlledTask task = new ControlledTask();
Thread t = new Thread(task);
t.start();
// ... 適當時機
task.requestStop(); // 通知線程退出迴圈

關鍵點:

  • volatile 確保主線程修改對工作線程可見
  • 配合 isInterrupted() 處理 interrupt() 呼叫
  • 避免使用已棄用的 stop()/suspend()/resume()(破壞鎖一致性)

五、完整範例:觀察狀態轉換

public class ThreadLifecycleDemo {
    public static void main(String[] args) throws InterruptedException {
        Thread t = new Thread(() -> {
            System.out.println("狀態: " + Thread.currentThread().getState()); // RUNNABLE
            try { Thread.sleep(1000); } catch (InterruptedException e) {}
            System.out.println("睡醒狀態: " + Thread.currentThread().getState()); // RUNNABLE
        });
        
        System.out.println("新建: " + t.getState()); // NEW
        t.start();
        System.out.println("啟動後: " + t.getState()); // RUNNABLE
        
        Thread.sleep(500);
        System.out.println("睡眠中: " + t.getState()); // TIMED_WAITING (睡眠)
        
        t.join();
        System.out.println("結束: " + t.getState()); // TERMINATED
    }
}

總結

  • 掌握 NEW → RUNNABLE → RUNNING ↔ BLOCKED → TERMINATED 狀態機。
  • 優先使用 Runnable + 線程池,Callable 用於需結果場景。
  • start() ≠ run(),yield() 不保證讓出。
  • 終止線程:volatile boolean 標誌位 + interrupt() 雙重保障。
  • 下一講將深入 同步機制、鎖優化、線程通訊。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved