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

Java 多執行緒實戰:Thread 與 Runnable 兩種創建方式完整範例

     編輯:Java 相關知識

創建執行緒是併發編程的第一步。Java 提供兩條路徑:實作 Runnable 介面與繼承 Thread 類。本文通過完整可運行範例,對比兩者優劣,並示範 start() vs run()、sleep()、join() 關鍵方法應用。

一、方式一:實作 Runnable 介面(推薦)


public class RunnableDemo {
    public static void main(String[] args) {
        // 1. 建立任務物件
        Task task = new Task("Worker-1");
        // 2. 包裝成 Thread 並啟動
        Thread t = new Thread(task, "Worker-1");
        t.start(); // 關鍵:呼叫 start() 才會開啟新執行緒執行 run()
        
        // 主執行緒同步執行
        for (int i = 5; i > 0; i--) {
            System.out.println("Main: " + i);
            sleep(1000);
        }
        System.out.println("Main thread exiting.");
    }
    
    static void sleep(long ms) {
        try { Thread.sleep(ms); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
    }
}

// 任務類:實作 Runnable,分離「做什麼」與「誰跑」
class Task implements Runnable {
    private final String name;
    Task(String name) { this.name = name; }
    
    @Override public void run() {
        for (int i = 5; i > 0; i--) {
            System.out.println(name + ": " + i);
            RunnableDemo.sleep(500); // 子執行緒較快
        }
        System.out.println(name + " exiting.");
    }
}

輸出範例(交錯順序視調度而定)


Worker-1: 5
Main: 5
Worker-1: 4
Worker-1: 3
Main: 4
Worker-1: 2
Worker-1: 1
Worker-1 exiting.
Main: 3
Main: 2
Main: 1
Main thread exiting.

二、方式二:繼承 Thread 類(不推薦常規使用)


class MyThread extends Thread {
    MyThread(String name) { super(name); } // 呼叫 Thread(String) 建構子設定名稱
    
    @Override public void run() {
        for (int i = 5; i > 0; i--) {
            System.out.println(getName() + ": " + i);
            try { Thread.sleep(500); } catch (InterruptedException e) { interrupt(); }
        }
        System.out.println(getName() + " exiting.");
    }
}

public class ExtendThreadDemo {
    public static void main(String[] args) {
        new MyThread("Worker-2").start();
        for (int i = 5; i > 0; i--) {
            System.out.println("Main: " + i);
            try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }
        }
        System.out.println("Main thread exiting.");
    }
}

三、核心差異對比

維度RunnableThread 繼承
耦合度低:任務與執行載體分離高:任務綁定執行緒
複用性高:同一 Runnable 可提交給多執行緒/線程池低:每次 new 新實例
繼承限制無:可繼承其他類受限:Java 單繼承,已佔用 extends
Lambda 支援完美(函數式介面)不支援
適用場景99% 場景、線程池、非同步任務極少數需覆寫 Thread 方法(如 interrupt() 行為)

四、關鍵方法深度解析

1. start() vs run()

  • start():啟動新執行緒,由 JVM 安排呼叫 run()。只能呼叫一次,再次呼叫拋 IllegalThreadStateException。
  • run():普通方法呼叫,在當前執行緒同步執行,**不會開啟新執行緒**。

2. sleep(long ms)

  • 靜態方法:Thread.sleep(ms) 讓**當前執行緒**進入 TIMED_WAITING。
  • **不釋放鎖**(持有 synchronized 鎖時 sleep 會阻塞其他執行緒)。
  • 必須捕捉 InterruptedException,**最佳實踐:捕捉後重設中斷標誌 Thread.currentThread().interrupt()**。

3. join() / join(long ms)

  • 等待目標執行緒終止(或超時)。底層呼叫 wait()。
  • 常用於主執行緒等待子任務完成:t.join();。

4. interrupt() 中斷協作機制


// 正確的中斷處理模式
while (!Thread.currentThread().isInterrupted()) {
    try { 
        Thread.sleep(1000); 
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt(); // 重設標誌
        break; // 或拋出、返回
    }
    doWork();
}

中斷是**請求**,非強制終止。被攔截方法(sleep/wait/join、I/O 阻塞)會拋異常/返回;純計算迴圈需主動檢查 isInterrupted()。

五、現代最佳實踐:ExecutorService 替代手動管理


// 固定大小線程池
ExecutorService executor = Executors.newFixedThreadPool(4);

// 提交 Runnable
executor.submit(() -> System.out.println("Task running on " + Thread.currentThread().getName()));

// 提交 Callable 獲取 Future
Future future = executor.submit(() -> { compute(); return 42; });
try { Integer result = future.get(5, TimeUnit.SECONDS); } 
catch (TimeoutException e) { future.cancel(true); }

// 關閉
executor.shutdown(); // 拒絕新任務,等待現有完成
executor.awaitTermination(1, TimeUnit.MINUTES);

優勢:生命週期管理、佇列緩衝、執行緒複用、異步結果、定時/週期任務(ScheduledExecutorService)。

結語

理解 Runnable 與 Thread 區別、start/run 機制、中斷協作模式,是寫出正確併發代碼的基石。實務上**直接使用 ExecutorService**,將任務提交邏輯與執行策略解耦,避免手動管理執行緒的坑。

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