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

Java 多線程系統總結:從創建到同步的完整實戰指南

     編輯:Java 相關知識

前言:進程與線程的本質區別

維度進程線程
資源隔離獨立代碼/數據空間同類線程共享代碼/數據
獨有資源完整地址空間獨立棧幀 + PC 計數器
切換開銷大(上下文切換)小(輕量級進程)
關係1 個進程包含 1~n 個線程線程屬於某個進程

多進程 = OS 同時運行多程序;多線程 = 單程序內多執行流並行。

一、兩種創建線程的標準姿勢

方式 1:繼承 Thread 類

class MyThread extends Thread {
    private final String name;
    public MyThread(String name) { this.name = name; }
    
    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println(name + " 執行:" + i);
            try { Thread.sleep((int)(Math.random() * 10)); }
            catch (InterruptedException e) { e.printStackTrace(); }
        }
    }
}

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

關鍵點:start() 啟動新線程異步執行 run();直接調用 run() 只會在主線程同步執行。同一 Thread 實例不可重複 start(),否則拋 IllegalThreadStateException。

方式 2:實現 Runnable 介面(推薦)

class MyRunnable implements Runnable {
    private final String name;
    public MyRunnable(String name) { this.name = name; }
    
    @Override
    public void run() {
        // 業務邏輯同 Thread 子類
    }
}

// 啟動:包裝成 Thread
new Thread(new MyRunnable("C")).start();
new Thread(new MyRunnable("D")).start();

優勢:

  1. 避免單繼承限制
  2. 同一 Runnable 實例可被多線程共享 → 實現資源共享(如賣票系統共享餘票數)
  3. 職責分離:任務邏輯與線程控制解耦

資源共享對比實驗

// Thread 子類:各實例獨有 count,無法共享
class Thread1 extends Thread { private int count = 5; ... }

// Runnable:共享同一實例 → 共享 count
class Thread2 implements Runnable { 
    private int count = 15; 
    public void run() { ... count-- ... }
}
Thread2 shared = new Thread2();
new Thread(shared, "C").start();
new Thread(shared, "D").start();
new Thread(shared, "E").start();

二、線程生命週期與狀態轉換


NEW --start()--> RUNNABLE <--running--> RUNNING
                    ↑  ↑          ↑
                    |  |          | sleep/yield/wait/join/I/O
                    |  |          |
                    |  +--notify/notifyAll/interrupt/timeout
                    |
                    +-- 获取 CPU 時間片
                    
RUNNING --run() 結束/異常--> TERMINATED

五大標準狀態:NEW、RUNNABLE、RUNNING、BLOCKED(含 WAITING/TIMED_WAITING)、TERMINATED。

阻塞三類型:

  1. 等待阻塞:wait() 進入等待池
  2. 同步阻塞:獲取鎖失敗進入鎖池
  3. 其他阻塞:sleep()、join()、I/O 操作

三、線程調度與控制藥箱

1. 優先級

  • 範圍 1~10:MIN=1、NORM=5、MAX=10
  • 繼承性:子線程默認繼承父線程優先級
  • 跨平台建議:僅使用三個常量,避免依賴 OS 具體映射

2. 睡眠 Thread.sleep(ms)

  • 釋放 CPU,進入 TIMED_WAITING
  • 不釋放鎖(若在 synchronized 內)
  • 超時自動轉 RUNNABLE

3. 等待 Object.wait()

  • 必須在 synchronized 內調用
  • 釋放鎖,進入等待池
  • 需 notify()/notifyAll() 喚醒

4. 讓步 Thread.yield()

  • RUNNING → RUNNABLE,讓同優先級線程執行
  • 不進入阻塞,不釋放鎖
  • 不可保證調度結果,可能立即再次獲取 CPU

5. 合併 join()

  • 主線程調用子線程 join() → 主線程阻塞,等待子線程終止
  • 典型場景:主線程需子線程計算結果才能繼續

6. 喚醒 notify()/notifyAll()

  • 隨機/全部喚醒等待池線程
  • 喚醒後需重新獲取鎖才能繼續
  • suspend()/resume() 已棄用(死鎖風險)

四、核心方法對比速查表

方法所屬類釋放鎖狀態轉換典型用途
sleep()Thread否RUNNING→TIMED_WAITING延遲、節流
wait()Object是RUNNING→WAITING線程協作
yield()Thread否RUNNING→RUNNABLE禮讓同優先級
join()Thread否RUNNING→WAITING等待線程結束

sleep() vs yield() 核心差異

  • sleep 進入阻塞,指定時間內肯定不執行;yield 回就緒,可能立即再次執行
  • sleep 允許低優先級線程搶佔;yield 僅讓同優先級

wait() vs sleep() 核心差異

  • wait 釋放鎖,sleep 持有鎖
  • wait 需 synchronized 配合,sleep 任意處調用
  • wait 需 notify 喚醒,sleep 到期自醒
  • 兩者均可被 interrupt() 中斷拋 InterruptedException

五、經典協作模式:三線程輪流打印 ABC


public class PrintABC {
    static class Printer implements Runnable {
        private final String name;
        private final Object prevLock, selfLock;
        
        Printer(String name, Object prev, Object self) {
            this.name = name; this.prevLock = prev; this.selfLock = self;
        }
        
        @Override public void run() {
            for (int i = 0; i < 10; i++) {
                synchronized (prevLock) {
                    synchronized (selfLock) {
                        System.out.print(name);
                        selfLock.notify();  // 喚醒下一個
                    }
                    try { prevLock.wait(); }  // 等待上一個
                    catch (InterruptedException e) { break; }
                }
            }
        }
    }
    
    public static void main(String[] args) throws Exception {
        Object a = new Object(), b = new Object(), c = new Object();
        new Thread(new Printer("A", c, a)).start();
        Thread.sleep(100);  // 確保啟動順序 A→B→C
        new Thread(new Printer("B", a, b)).start();
        Thread.sleep(100);
        new Thread(new Printer("C", b, c)).start();
    }
}

核心邏輯:每線程持有「前驅鎖」與「自我鎖」,雙重同步實現嚴格輪轉。notify() 釋放自我鎖喚醒下一個,wait() 釋放前驅鎖等待被喚醒。

六、線程術語小詞典

  • 主線程:JVM 啟動 main() 創建
  • 當前線程:Thread.currentThread() 獲取
  • 守護線程:為其他線程服務(如 GC),JVM 僅剩守護線程時退出
  • 用戶線程:非守護線程,JVM 等待所有用戶線程結束才退出
  • setDaemon(true):設置守護線程(需在 start() 前)

七、線程同步深度解析

synchronized 三大作用域

  1. 實例方法:鎖 this,同一實例互斥
  2. 靜態方法:鎖 Class 對象,類級別互斥
  3. 同步塊:synchronized(lockObj) { },自定義鎖粒度

進階鎖對象選擇


// 1. 實例鎖:同步塊
synchronized(this) { ... }

// 2. 專用鎖對象(推薦):零長度 byte 数组最經濟
private final byte[] lock = new byte[0];
synchronized(lock) { ... }

// 3. 類鎖:靜態方法或 synchronized(Foo.class)
synchronized(Foo.class) { ... }

關鍵規則:

  • 鎖對象必須唯一且共享
  • 靜態鎖與實例鎖互不干擾
  • synchronized 不可繼承,子類需顯式加鎖
  • 避免使用字串常量、基本類型包裝類作為鎖

死鎖預防鐵律

  1. 固定鎖獲取順序(如按 System.identityHashCode() 排序)
  2. 縮小同步塊範圍,減少持鎖時間
  3. 優先使用 java.util.concurrent 高階組件

八、線程數據傳遞三大範式

1. 構造器注入(啟動前準備就緒)


class MyThread extends Thread {
    private final String data;
    MyThread(String data) { this.data = data; }
    public void run() { System.out.println(data); }
}
new MyThread("init-data").start();

2. Setter 注入(啟動前/運行中動態設置)


class MyRunnable implements Runnable {
    private String data;
    public void setData(String data) { this.data = data; }
    public void run() { System.out.println(data); }
}
MyRunnable r = new MyRunnable();
r.setData("dynamic");
new Thread(r).start();

3. 回調函數(運行中產生數據反向傳遞)


interface Callback { void onResult(int sum); }

class Worker implements Runnable {
    private final Callback cb;
    Worker(Callback cb) { this.cb = cb; }
    public void run() {
        int n1 = new Random().nextInt(1000);
        int n2 = new Random().nextInt(2000);
        cb.onResult(n1 + n2);  // 異步回調
    }
}
new Thread(new Worker(sum -> System.out.println("Sum: " + sum))).start();

掌握上述核心知識體系,配合 java.util.concurrent 套件(線程池、鎖、並發集合、同步器)實戰演練,即可構建高性能、穩定的多線程應用。建議進階學習:AQS 原理、ThreadLocal 記憶體洩漏、CompletableFuture 異步編排、JVM 線程棧調優。

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