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

Java 線程創建與啟動全解析:Thread vs Runnable、常見誤區與最佳實踐

     編輯:Java 相關知識

一、線程創建兩大標準路徑

路徑 1:繼承 Thread 類


public class MyThread extends Thread {
    private final String name;
    
    public MyThread(String name) { this.name = name; }
    
    @Override
    public void run() {
        System.out.println(name + " 運行中...");
        // 業務邏輯
    }
}

// 啟動
new MyThread("Thread-A").start();

路徑 2:實現 Runnable 接口(推薦)


public class MyRunnable implements Runnable {
    private final String name;
    
    public MyRunnable(String name) { this.name = name; }
    
    @Override
    public void run() {
        System.out.println(name + " 運行中...");
    }
}

// 啟動:包裝成 Thread
new Thread(new MyRunnable("Runnable-B")).start();

// 匿名內部類
new Thread(() -> System.out.println("Lambda 線程")).start();

二、核心區別與選型理由

維度繼承 Thread實現 Runnable
繼承限制佔用單繼承名額無限制,可繼承其他類
資源共享每實例獨有字段,難共享同一實例可被多線程共享
職責分離線程控制與任務邏輯耦合任務邏輯獨立,配合線程池最佳
線程池適配不直接支持ExecutorService 原生支持
推薦場景簡單測試、教學演示生產代碼、並發任務

資源共享實戰對比


// Thread 子類:每個實例獨有 count,賣票系統會超賣
class TicketThread extends Thread {
    private int tickets = 100;
    public void run() { while(tickets>0) sell(); }
}
new TicketThread().start(); new TicketThread().start(); // 各賣 100 張!

// Runnable:共享同一實例,正確賣 100 張
class TicketRunnable implements Runnable {
    private int tickets = 100;
    public void run() { while(tickets>0) sell(); }
}
TicketRunnable task = new TicketRunnable();
new Thread(task).start(); new Thread(task).start(); new Thread(task).start();

三、啟動與運行關鍵機制

  1. start():異步啟動,JVM 分配線程資源,調度執行 run()
  2. run():普通方法調用,同步在當前線程執行,不是多線程
  3. 同一 Thread 實例只能 start() 一次,重複調用拋 IllegalThreadStateException
  4. 線程命名:new Thread(runnable, "名字") 或 thread.setName()
  5. 獲取當前線程:Thread.currentThread()

四、常見誤區避坑指南

誤區錯誤代碼正確代碼後果
調用 run() 代替 start()t.run();t.start();主線程同步執行,無並發效果
重複 start 同一實例t.start(); t.start();new Thread(r).start();IllegalThreadStateException
匿名類未啟動new Thread(() -> {});new Thread(() -> {}).start();線程對象創建但從未運行
主線程過早結束無 join/同步t.join(); / CountDownLatch守護線程隨 JVM 退出

五、線程命名與調試


// 設置有意義名稱,便於日誌追蹤、Arthas 診斷
Thread producer = new Thread(new Producer(), "Order-Producer-1");
Thread consumer = new Thread(new Consumer(), "Order-Consumer-1");

// 線程組批量管理(較少用)
ThreadGroup group = new ThreadGroup("Order-Processing");
new Thread(group, task, "Worker-1").start();

六、生產環境:線程池而非手動創建


// 避免頻繁創建銷毀開銷,防止 OOM
ExecutorService pool = Executors.newFixedThreadPool(10);  // 固定大小
// 或自定義參數
ThreadPoolExecutor pool = new ThreadPoolExecutor(
    10, 50, 60L, TimeUnit.SECONDS,
    new LinkedBlockingQueue<>(100),
    new ThreadFactoryBuilder().setNameFormat("Biz-Worker-%d").build(),
    new ThreadPoolExecutor.AbortPolicy()
);

pool.submit(() -> { /* 任務 */ });
pool.shutdown();  // 優雅關閉

七、完整示例:10 個線程並發打印


public class ThreadDemo {
    public static void main(String[] args) {
        System.out.println("主線程: " + Thread.currentThread().getName());
        
        for (int i = 1; i <= 10; i++) {
            final int num = i;
            new Thread(() -> {
                System.out.println("線程 " + num + " (" + Thread.currentThread().getName() + ") 啟動");
                try { Thread.sleep(100); } catch (InterruptedException ignored) {}
                System.out.println("線程 " + num + " 結束");
            }, "Worker-" + i).start();
        }
        
        // 主線程等待所有子線程(示例簡化,實際用 CountDownLatch)
        try { Thread.sleep(2000); } catch (InterruptedException ignored) {}
        System.out.println("主線程結束");
    }
}

輸出特徵:啟動順序固定,但運行順序隨機,由 OS 調度器決定,驗證並發非順序執行本質。


線程創建看似簡單,實則蘊含異步啟動機制、資源共享模型、線程池工程化等核心知識。牢記:任務邏輯實現 Runnable,執行控制交給 Thread/Executor,這是並發編程工程化的基石。

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