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

Java 多線程核心技術詳解:從入門到實戰

     編輯:Java 相關知識

前言

隨著多核處理器的普及,多線程程式設計已成為 Java 開發者必備的核心技能。本文將通過大量實戰代碼示例,系統講解 Java 多線程的創建方式、生命週期控制、同步機制、線程通信以及經典的生產者-消費者模型,幫助你構建穩健高效的併發應用。

一、創建線程的兩種基本方式

1. 繼承 Thread 類

定義子類繼承 java.lang.Thread,重寫 run() 方法作為線程執行體。注意:必須調用 start() 而非直接調用 run(),否則不會開啟新線程,只是在主線程中順序執行。

class HelloThread extends Thread {
    private String name;
    public HelloThread(String name) { this.name = name; }
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + " 執行 " + i);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        new HelloThread("A").start();
        new HelloThread("B").start();
    }
}

關鍵點:start() 內部通過 native 方法 start0() 請求作業系統分配 CPU 時間片,真正實現併發;直接調用 run() 只是普通方法調用。且 start() 重複調用會拋出 IllegalThreadStateException。

2. 實現 Runnable 接口(推薦)

實現 Runnable 接口,將任務邏輯放入 run(),再將實例傳給 Thread 構造器。這種方式解耦了「任務」與「線程控制」,優勢明顯:

  • 避免單繼承限制,類仍可繼承其他類
  • 便於實現資源共享(多線程操作同一 Runnable 實例)
  • 代碼與數據分離,增強健壯性
class HelloRunnable implements Runnable {
    private String name;
    public HelloRunnable(String name) { this.name = name; }
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + " 執行 " + i);
        }
    }
}

public class Main {
    public static void main(String[] args) {
        HelloRunnable task = new HelloRunnable("共享任務");
        new Thread(task).start();
        new Thread(task).start(); // 共享同一任務實例
    }
}

二、線程常用操作與狀態控制

1. 獲取與設置線程名稱

Thread.currentThread().getName(); // 獲取當前線程名
new Thread(task, "自定義名").start(); // 指定名稱

未指定名稱時,JVM 自動分配 Thread-0、Thread-1 等。主線程名稱為 main。

2. 判斷線程存活狀態

Thread t = new Thread(task);
System.out.println(t.isAlive()); // false
t.start();
System.out.println(t.isAlive()); // true

主線程結束不影響子線程繼續執行。

3. 線程強制執行

join() 使當前線程等待目標線程終止。

Thread t = new Thread(() -> {
    for (int i = 0; i < 3; i++) System.out.println("子線程 " + i);
});
t.start();
for (int i = 0; i < 50; i++) {
    if (i == 10) t.join(); // main 線程阻塞,直到 t 結束
    System.out.println("main " + i);
}

4. 線程休眠

Thread.sleep(millis) 讓當前線程暫時讓出 CPU,進入 TIMED_WAITING 狀態。

new Thread(() -> {
    for (int i = 0; i < 3; i++) {
        Thread.sleep(2000);
        System.out.println("醒來 " + i);
    }
}).start();

5. 線程中斷

interrupt() 只是設置中斷標誌;若線程處於阻塞狀態(sleep、wait、join),會拋出 InterruptedException 並清除標誌。

Thread t = new Thread(() -> {
    try { Thread.sleep(10000); }
    catch (InterruptedException e) { System.out.println("被中斷"); return; }
    System.out.println("正常結束");
});
t.start();
Thread.sleep(2000);
t.interrupt(); // 2秒後中斷

6. 守護線程

設置 setDaemon(true),當所有用戶線程結束,JVM 退出,守護線程自動終止。適用於後台監控、GC 等任務。

Thread daemon = new Thread(() -> {
    while (true) System.out.println("守護線程運行中...");
});
daemon.setDaemon(true);
daemon.start();

7. 線程優先級與禮讓

優先級範圍 1-10(默認 5),高優先級線程獲得 CPU 機率較大,但不保證先執行。yield() 暫停當前線程,讓同優先級線程執行。

Thread t1 = new Thread(task, "高優先級"); t1.setPriority(8);
Thread t2 = new Thread(task, "低優先級"); t2.setPriority(2);
t1.start(); t2.start();

// 禮讓示例
new Thread(() -> {
    for (int i = 0; i < 5; i++) {
        System.out.println(Thread.currentThread().getName() + " " + i);
        if (i == 3) Thread.yield();
    }
}, "A").start();

三、線程同步與死鎖

1. 同步代碼塊

使用 synchronized(obj) { ... },obj 為同步監視器(通常用 this)。同一時間只有一個線程持有鎖。

class TicketSeller implements Runnable {
    private int tickets = 5;
    @Override public void run() {
        for (int i = 0; i < 10; i++) {
            synchronized (this) {
                if (tickets > 0) {
                    System.out.println(Thread.currentThread().getName() + " 賣出第 " + tickets-- + " 張票");
                }
            }
        }
    }
}

2. 同步方法

在方法聲明上加 synchronized,非靜態方法鎖 this,靜態方法鎖類對象。

public synchronized void sale() {
    if (tickets > 0) System.out.println("賣出 " + tickets--);
}

3. 死鎖示例與預防

多線程互相持有對方需要的鎖並請求對方持有的鎖,導致永久等待。預防:按固定順序獲取鎖、使用 tryLock 超時機制等。

四、線程通信:wait / notify 實現生產者-消費者模型

僅靠同步無法協調「生產-消費」節奏,需引入 Object.wait()、notify() 實現等待/通知機制。必須在同步塊/方法中調用。

class SharedData {
    private String name; private int age; private boolean flag = false; // false: 可生產,true: 可消費

    public synchronized void produce(String n, int a) {
        while (flag) { try { wait(); } catch (InterruptedException e) {} }
        this.name = n; this.age = a;
        System.out.println("生產: " + name + " " + age);
        flag = true; notify();
    }

    public synchronized void consume() {
        while (!flag) { try { wait(); } catch (InterruptedException e) {} }
        System.out.println("消費: " + name + " " + age);
        flag = false; notify();
    }
}

class Producer implements Runnable {
    private SharedData data;
    Producer(SharedData data) { this.data = data; }
    @Override public void run() {
        for (int i = 0; i < 10; i++) {
            if (i % 2 == 0) data.produce("產品A", 100);
            else data.produce("產品B", 200);
        }
    }
}

class Consumer implements Runnable {
    private SharedData data;
    Consumer(SharedData data) { this.data = data; }
    @Override public void run() {
        for (int i = 0; i < 10; i++) data.consume();
    }
}

public class Main {
    public static void main(String[] args) {
        SharedData data = new SharedData();
        new Thread(new Producer(data)).start();
        new Thread(new Consumer(data)).start();
    }
}

輸出嚴格交替:生產→消費→生產→消費,完美解決數據不一致與重複讀取問題。

五、總結與最佳實踐

  • 優先選擇實現 Runnable/Callable 接口,配合線程池使用,而非直接繼承 Thread。
  • 同步範圍越小越好,避免過度同步導致性能下降或死鎖。
  • 線程通信首選 wait/notify 或更高級的 java.util.concurrent 組件(BlockingQueue、CountDownLatch、Semaphore 等)。
  • 合理設置守護線程、優先級,避免資源洩漏。
  • 掌握 ThreadLocal、volatile、Atomic* 等進階併發工具,應對複雜場景。

多線程是一門藝術,更是工程基石。持續實踐、深入原理,方能駕馭併發世界。

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