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

Java 多線程三大實現方式全解析:Runnable、TimerTask 與最佳實踐

     編輯:Java 相關知識

為什麼不只用「繼承 Thread」?

Java 單繼承限制導致「繼承 Thread」無法同時繼承業務基類。業界主流採用實作介面或組合框架方式,靈活解耦「任務邏輯」與「執行機制」。

方式二:實作 Runnable 介面(標準姿勢)

核心代碼

// 任務類:只負責「做什麼」
public class MyRunnable implements Runnable {
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try { Thread.sleep(1000); } catch (InterruptedException e) { return; }
            System.out.println("run:" + i);
        }
    }
}

// 啟動類:負責「怎麼跑」
public class Test2 {
    public static void main(String[] args) {
        MyRunnable task = new MyRunnable();      // 1. 建立任務
        Thread thread = new Thread(task);        // 2. 包裝成線程
        thread.start();                          // 3. 啟動
        
        // 主線程同步執行其他邏輯
        for (int i = 0; i < 10; i++) {
            try { Thread.sleep(1000); } catch (InterruptedException e) {}
            System.out.println("main:" + i);
        }
    }
}

關鍵優勢

  • 解耦:任務邏輯與執行緒分離,同一任務可提交給不同執行器(Thread、線程池、CompletableFuture)。
  • 無繼承限制:任務類仍可繼承其他業務基類。
  • 支援 Lambda:Java 8+ 可寫成 new Thread(() -> { ... }).start();

方式三:Timer + TimerTask(定時調度專用)

核心類別關係

  • Timer:單線程調度器,內部維護一條守護線程。
  • TimerTask:抽象類,實作 Runnable,承載具體任務邏輯。

基本用法:一次性延遲執行

import java.util.*;

public class MyTimerTask extends TimerTask {
    private final String prefix;
    public MyTimerTask(String prefix) { this.prefix = prefix; }
    
    @Override
    public void run() {
        for (int i = 0; i < 10; i++) {
            try { Thread.sleep(1000); } catch (InterruptedException e) { return; }
            System.out.println(prefix + i);
        }
    }
}

public class Test3 {
    public static void main(String[] args) {
        Timer timer = new Timer();
        TimerTask task = new MyTimerTask("線程1:");
        timer.schedule(task, 0); // 0ms 延遲立即執行
    }
}

四大 schedule 方法詳解

方法簽名語義典型場景
schedule(task, Date time)指定絕對時間點觸發一次每日零點備份
schedule(task, Date first, long period)指定時間點首次觸發,之後固定週期每天 10:00 發報表
schedule(task, long delay)延遲 delay ms 觸發一次5 秒後關閉閃屏頁
schedule(task, long delay, long period)延遲 delay 後首次觸發,之後固定週期App 啟動 3 秒後開始每 10 秒心跳

schedule vs scheduleAtFixedRate

  • schedule:「上次執行結束」+ period → 下次觸發。執行時間長會導致間隔變長(漂移)。
  • scheduleAtFixedRate:嚴格按「上次觸發時間」+ period 觸發。若執行超時,會趕緊補齊(可能併發執行)。

Timer 的重大侷限與解決方案

問題演示:單 Timer 多 Task 會串行阻塞


Timer t = new Timer();
t.schedule(new MyTimerTask("線程1:"), 1000);
t.schedule(new MyTimerTask("線程2:"), 1000);
// 輸出:線程1 跑完 10 秒後,線程2 才開始

原因:Timer 內部只有一條線程循環取任務執行,前一個任務未結束,後續任務只能排隊等待。

解決:多 Timer 實現真並發


new Timer().schedule(new MyTimerTask("線程1:"), 1000);
new Timer().schedule(new MyTimerTask("線程2:"), 1000);
// 兩條獨立 Timer 線程,真正並行

現代替代:ScheduledExecutorService


ScheduledExecutorService scheduler = Executors.newScheduledThreadPool(2);
scheduler.scheduleAtFixedRate(() -> System.out.println("任務1"), 0, 1, TimeUnit.SECONDS);
scheduler.scheduleAtFixedRate(() -> System.out.println("任務2"), 0, 1, TimeUnit.SECONDS);
// 線程池管理、異常隔離、關閉優雅、支援更多調度策略

總結建議

  1. 一般業務非同步任務:實作 Runnable + ExecutorService 提交。
  2. 簡單定時/週期任務:ScheduledExecutorService(取代 Timer)。
  3. 複雜排程(Cron 表達式、分散式):Quartz、Spring Task、XXL-Job。
  4. 避免直接繼承 Thread:除非極簡原型驗證,否則違反單一職責原則。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved