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

Java 線程狀態機與多線程實現方式深度解析

     編輯:Java 相關知識

線程六大狀態(JDK 1.5+ Thread.State)

Java 線程生命週期由六種狀態組成,非線性流轉:

NEW (新建)
    │ start()
    ▼
RUNNABLE (可運行) ◄──────────────────────────────┐
    │                                           │
    ├─ 獲取 CPU 時間片 ──▶ RUNNING (運行)        │
    │                                           │
    ├─ synchronized/lock 等待 ──▶ BLOCKED (阻塞)  │
    │                                           │
    ├─ Object.wait() ──▶ WAITING (無限等待)     │
    │       │                                    │
    │       └─ 超時等待 ──▶ TIMED_WAITING (限時等待) │
    │                                           │
    └─ run() 結束/異常 ──▶ TERMINATED (終止) ◄──┘

狀態詳細解析

狀態觸發條件退出條件備註
NEWnew Thread()調用 start()未啟動,不佔用 CPU
RUNNABLEstart()、鎖釋放、notify、睡眠結束獲得 CPU 執行、等待鎖、調用 wait/sleep包含就緒與運行,JVM 不區分
BLOCKED嘗試進入 synchronized 方法/塊,鎖被佔用獲得鎖只針對 synchronized,Lock 導致 WAITING
WAITINGObject.wait()、Thread.join()、LockSupport.park()notify/notifyAll、被 join 線程結束、LockSupport.unpark無超時,需外部喚醒
TIMED_WAITINGThread.sleep()、wait(timeout)、join(timeout)、parkNanos/parkUntil超時、被喚醒帶超時參數的等待
TERMINATEDrun() 正常返回、拋出未捕獲異常不可逆線程生命週期結束

關鍵狀態轉換觸發點

// NEW -> RUNNABLE
Thread t = new Thread(() -> {}); // NEW
t.start(); // RUNNABLE

// RUNNABLE -> BLOCKED
synchronized(lock) { // 獲取鎖
    // 持有鎖期間其他線程進入此塊 -> BLOCKED
}

// RUNNABLE -> WAITING
synchronized(lock) {
    lock.wait(); // 釋放鎖進入 WAITING
}

// RUNNABLE -> TIMED_WAITING
Thread.sleep(1000); // 進入 TIMED_WAITING
lock.wait(2000);    // 進入 TIMED_WAITING
t.join(3000);       // 進入 TIMED_WAITING

// WAITING/TIMED_WAITING -> RUNNABLE
lock.notify();      // 喚醒一個 WAITING 線程
lock.notifyAll();   // 喚醒所有
LockSupport.unpark(thread); // 喚醒 park 線程

// RUNNABLE -> TERMINATED
// run() 方法執行完畢或拋出異常

多線程實現的兩種核心方式

方式一:繼承 Thread 類

class MyThread extends Thread {
    @Override
    public void run() {
        // 線程執行邏輯
        for (int i = 0; i < 5; i++) {
            System.out.println(getName() + " - " + i);
        }
    }
}

// 使用
new MyThread().start();
new MyThread().start();
  • 優點:簡單直觀,this.getName() 直接獲取線程名
  • 缺點:單繼承限制,線程邏輯與控制耦合,無法共享數據邏輯

方式二:實現 Runnable 接口(推薦)

class MyTask implements Runnable {
    private int counter = 10; // 共享數據
    
    @Override
    public void run() {
        while (counter > 0) {
            System.out.println(Thread.currentThread().getName() + " 賣票: " + counter--);
        }
    }
}

// 使用:多線程共享同一任務實例
MyTask task = new MyTask();
new Thread(task, "窗口A").start();
new Thread(task, "窗口B").start();
new Thread(task, "窗口C").start();
  • 優點:解耦任務與線程控制、支持多線程共享數據(賣票案例)、無單繼承限制、可提交線程池
  • 缺點:獲取線程名需 Thread.currentThread()

方式三:實現 Callable + FutureTask(有返回值/拋異常)

class MyCallable implements Callable {
    @Override
    public Integer call() throws Exception {
        Thread.sleep(1000);
        return 42; // 返回結果
    }
}

// 使用
FutureTask ft = new FutureTask<>(new MyCallable());
new Thread(ft).start();
Integer result = ft.get(); // 阻塞獲取結果
System.out.println("Result: " + result);

Thread vs Runnable 核心對比

維度Thread 繼承Runnable 實現
本質類繼承接口實現
數據共享困難(每個 Thread 獨立實例)天然支持(同一 Runnable 實例)
擴展性受單繼承限制可同時實現多接口、繼承其他類
線程池提交不可直接提交直接提交 executor.submit(runnable)
推薦場景簡單獨立線程、教學演示生產環境、任務邏輯復用、線程池

線程常用操作 API

// 啟動
thread.start(); // 絕不調用 run()!

// 休眠(靜態方法,當前線程)
Thread.sleep(1000); // 拋 InterruptedException

// 讓出 CPU(靜態,非阻塞,僅提示調度器)
Thread.yield();

// 等待其他線程結束
t.join(); // 或 join(5000)

// 中斷(協作式)
thread.interrupt(); // 設置中斷標誌
// 線程內需檢查:Thread.interrupted() 或 isInterrupted()
// 睡眠/等待時拋出 InterruptedException

// 守護線程(JVM 僅剩守護線程則退出)
thread.setDaemon(true); // 必須 start() 前設置

// 線程名
thread.setName("Worker-1");
Thread.currentThread().getName();

// 線程優先級(建議勿依賴)
thread.setPriority(Thread.MAX_PRIORITY);

經典賣票案例對比

Thread 繼承:每窗口獨立 10 張票

class TicketWindow extends Thread {
    private int tickets = 10;
    public void run() {
        while (tickets > 0) System.out.println(getName() + " 出票: " + tickets--);
    }
}
// 三窗口各賣 10 張,共 30 張

Runnable 共享:三窗口共賣 10 張票

class TicketSeller implements Runnable {
    private int tickets = 10;
    public void run() {
        while (tickets > 0) System.out.println(Thread.currentThread().getName() + " 出票: " + tickets--);
    }
}
TicketSeller seller = new TicketSeller();
new Thread(seller, "A").start();
new Thread(seller, "B").start();
new Thread(seller, "C").start();
// 共賣 10 張,體現數據共享

最佳實踐原則

  1. 優先實現 Runnable/Callable,解耦任務邏輯,支持線程池、數據共享
  2. 使用線程池:ExecutorService 管理線程生命週期,避免頻繁創建銷毀
  3. 正確處理中斷:捕獲 InterruptedException 後重新設置中斷標誌或退出
  4. 避免直接操作 Thread 狀態:stop()/suspend()/resume() 已棄用,不安全
  5. 守護線程用於輔助任務:日誌、監控、GC 等
  6. 線程命名規範:業務-序號,如 Order-Processor-1,便於排查

總結

Java 線程六大狀態(NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED)構成生命週期狀態機。多線程實現強烈推薦 Runnable/Callable + 線程池,避免 Thread 繼承的單繼承限制與耦合問題。掌握狀態轉換觸發點(start/sleep/wait/join/notify/interrupt)、協作式中斷模式、守護線程語義,是編寫正確並發程序的基石。

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