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

Java 多線程核心技術全景詳解:從創建到通信的系統掌握

     編輯:Java 相關知識

前言

線程是操作系統調度的基本單位,Java 內置多線程支持是其核心優勢。本文系統覆蓋線程屬性、三種創建方式、同步協作(wait/notify、sleep/interrupt、join)、線程通信(管道流)、守護線程、優先級等核心知識,附帶完整可運行代碼示例。

一、線程基礎屬性

Thread t = Thread.currentThread();
String name = t.getName();                    // 線程名
int priority = t.getPriority();               // 優先級 1-10,默認 5
ThreadGroup group = t.getThreadGroup();       // 線程組
boolean isDaemon = t.isDaemon();              // 是否守護線程

// 修改屬性
t.setName("Worker-1");
t.setPriority(Thread.MAX_PRIORITY);
t.setDaemon(true); // 必須在 start() 前調用

關鍵屬性解讀

屬性說明
名字默認 "Thread-",建議業務語義命名便於診斷
優先級JVM 搶佔式調度,高優先級優先獲得 CPU,同優先級時間片輪轉
線程組樹狀結構管理,支持批量中斷、異常處理,現代較少直接使用
守護線程true:父線程結束時 JVM 強制終止子線程(如 GC);false:用戶線程,JVM 等待所有用戶線程結束才退出

守護線程演示

Thread daemon = new Thread(() -> {
    Thread sub = new Thread(() -> {
        for (int i = 0; i < 100; i++) System.out.println("Sub: " + i);
    });
    sub.setDaemon(true); // 子守護
    sub.start();
    System.out.println("Daemon main end");
});
daemon.start();
// 設置 daemon.setDaemon(true) 會導致 sub 隨主線程結束而終止

二、三種創建線程方式

1. 繼承 Thread 類


class MyThread extends Thread {
    @Override public void run() {
        for (int i = 0; i < 5; i++) System.out.println("Thread " + getName() + " running");
    }
}
new MyThread().start();

2. 實現 Runnable 接口


class MyRunnable implements Runnable {
    @Override public void run() {
        for (int i = 0; i < 5; i++) System.out.println("Thread " + Thread.currentThread().getName() + " running");
    }
}
new Thread(new MyRunnable()).start();

3. 匿名內部類 / Lambda(本質同 Runnable)


new Thread(() -> {
    for (int i = 0; i < 5; i++) System.out.println("Lambda thread running");
}).start();

核心差異:多對象多線程 vs 單對象多線程

模式Thread 繼承 / 匿名類Runnable 實現
對象關係每個線程一個獨立對象可共享同一 Runnable 實例
同步鎖各自鎖(this 不同)共享鎖(同一 Runnable 實例)
適用場景線程間無共享狀態線程間需共享數據/協作

notify/notifyAll 實戰驗證


// 多對象多線程:每個線程獨立鎖,notify 喚醒各自
MyThread[] threads = new MyThread[3];
for (int i = 0; i < 3; i++) {
    threads[i] = new MyThread(); threads[i].start(); threads[i].wait();
}
for (MyThread t : threads) synchronized(t) { t.notify(); }

// 單對象多線程:共享 Runnable 實例,需 notifyAll 喚醒所有
MyRunnable shared = new MyRunnable();
Thread[] ts = new Thread[3];
for (int i = 0; i < 3; i++) { ts[i] = new Thread(shared); ts[i].start(); }
synchronized(shared) { shared.notifyAll(); } // 必須用 notifyAll

三、線程狀態轉換


NEW -> start() -> RUNNABLE (就緒/運行)
RUNNABLE -> sleep()/wait()/join()/I/O -> BLOCKED/WAITING/TIMED_WAITING
BLOCKED/WAITING -> 鎖釋放/notify/超時 -> RUNNABLE
RUNNABLE -> run() 結束/stop()/異常 -> TERMINATED

六大標準狀態:NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。

四、核心協作機制

1. wait / notify / notifyAll

  • 必須在 synchronized(obj) 塊中調用 obj.wait()/notify()
  • wait() 釋放鎖並進入 Wait Set;notify() 隨機喚醒一個;notifyAll() 喚醒全部
  • 喚醒後需重新競爭鎖,進入 Entry Set
  • 標準模式:while(condition) wait(); 防虛假喚醒

2. sleep / interrupt


Thread.sleep(5000); // 休眠 5 秒,不釋放鎖
// 中斷休眠
Thread t = new Thread(() -> {
    try { Thread.sleep(60000); } catch (InterruptedException e) { return; }
});
t.start(); Thread.sleep(500); t.interrupt(); // 拋出 InterruptedException

最佳實踐:捕獲 InterruptedException 後 重新設置中斷標誌 (Thread.currentThread().interrupt()) 或直接返回終止線程。

3. join 等待線程終止


Thread t = new Thread(() -> { /* 長任務 */ });
t.start();
// 主線程做其他事...
t.join(); // 阻塞直到 t 結束
System.out.println("主線程繼續");

4. yield 讓出 CPU


Thread.yield(); // 暫停當前時間片,重新參與調度,不保證立即切換

五、線程間通信:管道流

利用 PipedInputStream/OutputStream 或 PipedReader/Writer 實現線程間字節/字符流通信,底層為環形緩衝區。

字節流示例


PipedOutputStream pos = new PipedOutputStream();
PipedInputStream pis = new PipedInputStream(pos);

// 寫入線程
new Thread(() -> {
    try (BufferedReader br = new BufferedReader(new InputStreamReader(System.in))) {
        String line;
        while (!(line = br.readLine()).equals("end")) {
            pos.write(line.getBytes());
        }
    } finally { pos.close(); }
}).start();

// 讀取線程
new Thread(() -> {
    try {
        byte[] buf = new byte[1024];
        int len;
        while ((len = pis.read(buf)) != -1) {
            System.out.println("收到: " + new String(buf, 0, len));
        }
    } finally { pis.close(); }
}).start();

字符流示例(推薦)


PipedWriter pw = new PipedWriter();
PipedReader pr = new PipedReader(pw);
BufferedWriter bw = new BufferedWriter(pw);
BufferedReader br = new BufferedReader(pr);

// 寫入線程
new Thread(() -> {
    try (BufferedReader sysIn = new BufferedReader(new InputStreamReader(System.in))) {
        String line;
        while (!(line = sysIn.readLine()).equals("end")) {
            bw.write(line); bw.newLine(); bw.flush();
        }
    } catch (IOException e) { e.printStackTrace(); }
}).start();

// 讀取線程
new Thread(() -> {
    try {
        String line;
        while ((line = br.readLine()) != null) {
            System.out.println("收到: " + line);
        }
    } catch (IOException e) { e.printStackTrace(); }
}).start();

六、線程安全終止模式


class Worker implements Runnable {
    private volatile boolean running = true;

    @Override public void run() {
        while (running && !Thread.currentThread().isInterrupted()) {
            // 業務邏輯
            try { Thread.sleep(1000); } catch (InterruptedException e) { break; }
        }
        // 清理資源
    }

    public void shutdown() { running = false; }
}

// 使用
Worker w = new Worker();
Thread t = new Thread(w);
t.start();
// ... 稍後終止
w.shutdown(); // 或 t.interrupt();
t.join();

核心原則:

  1. 使用 volatile boolean 標誌或 interrupt() 發出終止信號
  2. 線程內部定期檢查標誌/中斷狀態
  3. 阻塞操作捕獲 InterruptedException 並優雅退出
  4. 絕不使用 Thread.stop() / suspend() / resume()(已棄用,不安全)

七、總結與現代建議

傳統 API現代替代優勢
Thread / RunnableExecutorService / CompletableFuture線程池復用、任務提交解耷、異步組合、異常處理
synchronized / wait/notifyLock / Condition / Semaphore可中斷、超時、公平、多條件、非塊結構
volatile / 手工同步Atomic* / LongAdder / ConcurrentHashMap無鎖 CAS、高併發計數器、線程安全集合
PipedStreamBlockingQueue / Disruptor / Reactor Streams背壓、批量、非阻塞、函數式組合

掌握基礎原理是為了正確選型、排查死鎖/活鎖/饑餓、理解框架源碼。生產代碼中,優先使用 java.util.concurrent 高級組件,將同步協作複雜度交給專家級庫,專注業務價值創造。

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