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

Java 多線程基礎:線程定義、狀態轉換與屬性詳解

     編輯:Java 相關知識

前言

多線程是 Java 並發編程的基石。本文系統講解線程與進程區別、三種創建方式、生命周期狀態轉換、優先級與守護線程,為進階並發學習打牢基礎。

一、進程與線程核心區別

維度進程線程
定義程序執行的實例,資源分配單位CPU 調度單位,輕量級進程
內存空間獨立地址空間共享進程堆/方法區,私有棧/程序計數器
創建開銷大(需分配獨立資源)小(共享資源)
切換開銷大(上下文切換重)小
通信方式IPC(管道、Socket、共享內存)共享內存、volatile、鎖、隊列
穩定性崩潰不影響其他進程一線程崩潰可導致整進程退出

為什麼需要多線程?

  1. 提升響應性:耗時任務異步化,避免主線程阻塞
  2. 榨乾 CPU:多核並行,提高吞吐量
  3. 簡化建模:異步事件、定時任務、生產消費者模型自然映射
  4. 資源利用率:I/O 等待期切換執行其他任務

二、三種創建線程方式

方式 1:繼承 Thread 類


public class MyThread extends Thread {
    @Override
    public void run() {
        System.out.println("Thread running: " + Thread.currentThread().getName());
    }
}

// 使用
Thread t = new MyThread();
t.start(); // 必須調用 start(),而非 run()

缺點:Java 單繼承限制,線程邏輯與控制耦合。

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


public class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println("Runnable task: " + Thread.currentThread().getName());
    }
}

// 使用
Thread t = new Thread(new MyTask());
t.start();

// 或配合線程池
ExecutorService executor = Executors.newFixedThreadPool(10);
executor.submit(new MyTask());

優勢:分離任務邏輯與執行控制,支持線程池復用,可實現多個接口。

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


public class MyCallable implements Callable<String> {
    @Override
    public String call() throws Exception {
        Thread.sleep(1000);
        return "Result from " + Thread.currentThread().getName();
    }
}

// 使用
ExecutorService executor = Executors.newSingleThreadExecutor();
Future<String> future = executor.submit(new MyCallable());

try {
    String result = future.get(); // 阻塞獲取結果
    System.out.println(result);
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
} finally {
    executor.shutdown();
}

對比總結

特性ThreadRunnableCallable
返回值無無有 (泛型)
拋出異常無無可拋出
線程池支持差好好
適用場景簡單繼承場景通用任務需結果/異常的任務

三、線程生命周期與狀態轉換

六大狀態(Thread.State 枚舉)


public enum State {
    NEW,           // 新建:創建未 start()
    RUNNABLE,      // 可運:含就緒+運行,爭搶 CPU
    BLOCKED,       // 阻塞:等待監視器鎖
    WAITING,       // 等待:無超時等待 (wait/join/park)
    TIMED_WAITING, // 定時等待:有超時 (sleep/wait/join/parkNanos)
    TERMINATED     // 終止:run() 結束或異常退出
}

狀態轉換圖解


                    +------------------+
                    |      NEW         |  (new Thread())
                    +--------+---------+
                             |
                          start()
                             v
                    +--------+---------+
                    |    RUNNABLE      |<-------------------+
                    +--------+---------+                    |
                             |                             |
                  +----------+----------+                  |
                  |                     |                  |
           获得 CPU           釋放 CPU/時間片           等待鎖/調用 wait()
                  |                     |                  |
                  v                     v                  v
            +-------+            +--------------+   +-------------+
            | RUNNING |          | TIMED_WAITING |   |   BLOCKED   |
            +-------+            +--------------+   +-------------+
                  |                     |                  |
         結束/異常|              超時/notify          獲得鎖
                  |                     |                  |
                  v                     v                  v
                    +--------+---------+
                    |   TERMINATED     |
                    +------------------+

關鍵方法影響

方法從狀態到狀態釋放鎖備註
start()NEWRUNNABLE-異步啟動
sleep()RUNNABLETIMED_WAITING否靜態方法,當前線程
wait()RUNNABLEWAITING是需在 synchronized 內
wait(timeout)RUNNABLETIMED_WAITING是
join()RUNNABLEWAITING否等待目標線程結束
lock.lock()RUNNABLEBLOCKED-獲取鎖阻塞
notify/notifyAllWAITINGRUNNABLE-喚醒等待線程
run() 結束RUNNABLETERMINATED-正常或異常退出

四、線程終止的正確姿勢

❌ 錯誤方式:stop()/suspend()/resume()

已棄用,會導致鎖強制釋放、數據不一致、死鎖風險。

✅ 正確方式:中斷標誌 + 協作式終止


public class GracefulShutdownTask implements Runnable {
    @Override
    public void run() {
        while (!Thread.currentThread().isInterrupted()) { // 檢查中斷標誌
            try {
                doWork();
                Thread.sleep(1000); // 可中斷阻塞
            } catch (InterruptedException e) {
                // 睡眠中被中斷,標誌被清除,需重新設置
                Thread.currentThread().interrupt(); // 重新設置標誌
                break; // 退出循環
            }
        }
        cleanup(); // 清理資源
    }
    
    private void doWork() { /* ... */ }
    private void cleanup() { /* ... */ }
}

// 使用
Thread t = new Thread(new GracefulShutdownTask());
t.start();
// ... 
t.interrupt(); // 請求中斷

關鍵原則

  1. 阻塞方法(sleep/wait/join)拋出 InterruptedException 時清除中斷標誌,需手動 `interrupt()` 恢復
  2. 非阻塞代碼需主動檢查 `isInterrupted()`
  3. 捕獲 InterruptedException 後不要吞沒,重新設置標誌或向外拋出

五、線程優先級與守護線程

優先級

  • 範圍:1 (MIN_PRIORITY) ~ 10 (MAX_PRIORITY),默認 5 (NORM_PRIORITY)
  • 繼承父線程優先級
  • 警告:優先級只是調度提示,不可依賴實現業務邏輯;高優先級線程可能導致低優先級「餓死」

守護線程


Thread daemon = new Thread(() -> {
    while (true) {
        System.out.println("Daemon running...");
        try { Thread.sleep(1000); } catch (InterruptedException e) { break; }
    }
});
daemon.setDaemon(true); // 必須在 start() 前設置
daemon.start();
  • JVM 只剩守護線程時自動退出
  • 典型場景:GC、JIT 編譯、監控線程、心跳檢測、緩存清理
  • 不要在守護線程執行關鍵業務邏輯(如持久化),可能來不及完成

六、常見面試題精選

  1. sleep vs wait:sleep 不釋放鎖、屬於 Thread、指定時間;wait 釋放鎖、屬於 Object、需 notify 喚醒
  2. start vs run:start 啟動新線程異步執行 run;run 直接調用同步執行
  3. 如何實現線程按序執行:join()、CountDownLatch、CompletableFuture.thenRun()
  4. volatile 能保證線程安全嗎:只保證可見性+有序性,不保證原子性(如 i++)
  5. 守護線程與用戶線程區別:JVM 退出判定依據

總結

掌握線程創建方式選擇(優先 Runnable/Callable)、生命周期狀態轉換規則、協作式中斷模式、守護線程特性,是 Java 並發編程的入門必修課。下一步建議深入學習:同步機制(synchronized/Lock)、JUC 工具類、線程池原理、JMM 內存模型。

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