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

Java多線程編程基礎(二):線程對象核心方法與Runnable模式深度解析

     編輯:Java 相關知識

前言:線程對象方法是並發控制的基礎原語

上文確立Thread對象與OS線程的區別。本文深度解析Thread/Runnable核心方法、join()語義、start()一次性限制、Runnable共享狀態模式,為後續同步、協作、線程池奠基。

一、 Thread vs Runnable:本質區別與選型

維度繼承Thread實現Runnable
線程對象與任務強耦合(同一對象)解耦(任務獨立)
單繼承限制佔用無影響
start()次數一次(對象綁定線程)多次(同一Runnable可包裝多Thread)
共享狀態需額外字段/外部對象天然共享Runnable實例字段
線程池適配不直接支持完美適配ExecutorService
函數式編程不支持Lambda() -> {}、方法引用

結論:優先Runnable,配合ExecutorService或Thread.ofVirtual()(Java 21+虛擬線程)。

二、 start()一次性限制:源碼證據


// Thread.java (JDK 17)
public synchronized void start() {
    if (threadStatus != 0)  // NEW=0
        throw new IllegalThreadStateException();
    group.add(this);
    boolean started = false;
    try {
        start0(); // native
        started = true;
    } finally {
        if (!started) group.threadStartFailed(this);
    }
}

threadStatus從NEW(0)→RUNNABLE→TERMINATED單向流轉。**二次start()拋IllegalThreadStateException**。

三、 join()深度語義:等待線程終止


public final void join() throws InterruptedException {
    join(0);
}
public final synchronized void join(long millis) throws InterruptedException {
    long base = System.currentTimeMillis();
    long now = 0;
    if (millis < 0) throw new IllegalArgumentException();
    if (millis == 0) {
        while (isAlive()) wait(0); // 死等
    } else {
        while (isAlive()) {
            long delay = millis - now;
            if (delay <= 0) break;
            wait(delay);
            now = System.currentTimeMillis() - base;
        }
    }
}

關鍵點:

  • 調用t.join()的線程(當前線程)阻塞在t對象監視器上
  • t線程結束時,JVM調用t.notifyAll()喚醒等待者
  • join(millis)帶超時,超時返回不拋異常,需檢查t.isAlive()判斷是否結束
  • 必須在synchronized(t)塊中調用(方法聲明synchronized),否則IllegalMonitorStateException

四、 Runnable共享狀態模式與線程安全


class CounterTask implements Runnable {
    private int count = 0; // 共享狀態!
    @Override public void run() {
        for (int i = 0; i < 10000; i++) count++;
        System.out.println(Thread.currentThread().getName() + ": " + count);
    }
}

public class SharedRunnable {
    public static void main(String[] args) throws InterruptedException {
        CounterTask task = new CounterTask();
        Thread t1 = new Thread(task, "T1");
        Thread t2 = new Thread(task, "T2");
        t1.start(); t2.start();
        t1.join(); t2.join();
        System.out.println("Final: " + task.count); // 非20000!
    }
}

現象:count++非原子(讀-改-寫),兩線程競爭導致更新丟失,結果<20000。

修復:

  1. AtomicInteger:atomicCount.incrementAndGet()
  2. synchronized:synchronized(this) { count++; }
  3. ThreadLocal:各線程獨立副本,最後聚合
  4. 不可變設計:無共享狀態

五、 線程常用方法速查表

方法靜態/實例作用注意事項
currentThread()靜態獲取當前執行線程對象核心API,用於ThreadLocal、日誌、監控
sleep(millis/nanos)靜態當前線程定時讓出CPU不釋放鎖、拋InterruptedException需處理
yield()靜態提示調度器重新調度不保證讓步、平台差異大、慎用
interrupt()實例設置中斷標誌不強制停止,阻塞方法收到InterruptedException
isInterrupted()實例檢查中斷標誌(不清除)用於輪詢判斷
interrupted()靜態檢查並清除當前線程中斷標誌唯一清除標誌的方法
setDaemon(true)實例標記為守護線程必須在start()前調用,JVM僅剩守護線程退出
getState()實例獲取線程狀態(NEW/RUNNABLE/BLOCKED/WAITING/TIMED_WAITING/TERMINATED)監控、診斷用
getStackTrace()實例獲取線程棧幀快照定位死鎖、卡頓、性能分析
setUncaughtExceptionHandler()實例/靜態設置未捕獲異常處理器線程池建議設置默認處理器

六、 守護線程與JVM退出機制


public class DaemonDemo {
    public static void main(String[] args) {
        Thread daemon = new Thread(() -> {
            while (true) {
                System.out.println("Daemon running...");
                try { Thread.sleep(500); } catch (InterruptedException e) { break; }
            }
        });
        daemon.setDaemon(true); // 必須在start()前
        daemon.start();
        System.out.println("Main ends"); // 主結束,JVM退出,daemon被強制終止
    }
}

規則:JVM進程在所有非守護線程結束後退出,不等待守護線程。典型用途:GC、JIT、監控、心跳、清理任務。

七、 總結與下一站

本文掌握:

  1. Runnable共享實例 = 共享狀態 = 線程安全風險 → 原子類/鎖/不可變
  2. join() = 監視器等待 + 線程結束notifyAll → 順序協調原語
  3. start()一次性 = 狀態機單向流轉 → 線程池複用線程對象,而非重啟
  4. 中斷協作式 = 標誌 + 阻塞方法響應 → 正確停止模式

下一站:同步與協作核心——synchronized、wait/notify、Lock、Condition、內存模型、volatile、原子類。

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