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

Java 多線程核心精講:從線程創建到同步機制的完整實戰指南

     編輯:Java 相關知識

多線程是 Java 併發編程的基石。本文將系統梳理線程的兩種創建方式、啟動機制、同步原理及鎖優化策略,並通過大量代碼示例揭示常見誤區,幫助開發者構建穩健的併發程序。

一、線程創建的兩條路徑

1. 繼承 Thread 類

重寫 run() 方法定義任務邏輯,調用 start() 啟動線程。切記:絕不能直接調用 run(),否則會在主線程中同步執行,喪失併發性。start() 內部會完成線程註冊、棧分配等初始化工作,然後由 JVM 調度執行 run()。

public class MyThread extends Thread {
    @Override
    public void run() { /* 任務代碼 */ }
}
// 使用
new MyThread().start();

2. 實現 Runnable 接口(推薦)

將任務邏輯封裝在 run() 中,再將實例傳給 Thread 構造器。此方式解耦任務與線程控制,便於共享數據、避免單繼承限制,也是線程池提交任務的標準形式。

public class MyTask implements Runnable {
    @Override
    public void run() { /* 任務代碼 */ }
}
// 使用
new Thread(new MyTask()).start();

關鍵細節:線程對象與 GC

即使沒有引用變量持有 Thread 對象,JVM 內部仍會註冊線程引用,因此線程對象不會被 GC 回收,直到線程終止。

二、同步機制深度解析

1. 為什麼需要同步?

多線程共享堆內存中的實例變量、靜態變量時,非原子操作(如 i++、long/double 讀寫)會導致數據競爭,產生「臟讀」、「丟失更新」等一致性問題。

2. synchronized 關鍵字:內置鎖(Monitor)

  • 實例方法鎖:鎖對象是 this,同一實例的不同線程互斥。
  • 靜態方法鎖:鎖對象是 Class 對象,所有實例共享此鎖。
  • 同步代碼塊:synchronized(obj) { ... },手動指定鎖對象,粒度更細。

3. 鎖的對象層級與類層級獨立

對象鎖(非 static synchronized)與類鎖(static synchronized)互不干擾。同一線程可同時持有對象鎖和類鎖;不同線程分別持有對象鎖和類鎖時可並行執行。

4. 同步粒度優化:縮小鎖範圍

僅同步訪問共享變量的核心代碼,而非整個方法。例如:

// 粗粒度:整個方法同步
public synchronized void update() { ... }

// 細粒度:僅同步關鍵區
public void update() {
    // 非共享操作...
    synchronized(this) { sharedVar++; }
    // 非共享操作...
}

細粒度鎖能顯著降低線程爭用,提升併發吞吐量。

5. 經典錯誤:鎖對象不穩定

在同步塊中重新賦值鎖對象(lock = new Object())會導致每次同步使用不同鎖,完全失效。解決方案:將鎖對象聲明為 final,或使用單例模式獲取固定鎖實例。

三、線程間協作:wait / notify / notifyAll

基於對象鎖的等待/通知機制:

  1. 線程持有鎖調用 wait() 釋放鎖並進入等待隊列。
  2. 另一線程獲取同一鎖,完成條件變更後調用 notify()(喚醒一個)或 notifyAll()(喚醒全部)。
  3. 被喚醒線程重新競爭鎖,獲得後從 wait() 返回繼續執行。

核心規範:wait/notify 必須在 synchronized 塊中調用,否則拋出 IllegalMonitorStateException;建議使用 while(條件不滿足) wait() 防止虛假喚醒。

四、生產者-消費者模式實戰

利用共享鎖 + wait/notify 實現無鎖隊列的線程安全協作:

class Buffer {
    final Object lock = new Object();
    List<Task> queue = new ArrayList<>();
    final int capacity;

    public void produce(Task t) throws InterruptedException {
        synchronized(lock) {
            while(queue.size() == capacity) lock.wait();
            queue.add(t);
            lock.notifyAll();
        }
    }
    public Task consume() throws InterruptedException {
        synchronized(lock) {
            while(queue.isEmpty()) lock.wait();
            Task t = queue.remove(0);
            lock.notifyAll();
            return t;
        }
    }
}

五、避坑指南總結

  • 優先使用 Runnable + 線程池,避免手動管理線程生命週期。
  • 同步鎖對象務必 final,防止引用變更導致同步失效。
  • 採用細粒度鎖,縮小同步塊範圍,提升併發性能。
  • wait/notify 配合 while 循環使用,確保條件滿足後再執行。
  • 跨類共享鎖時,通過構造器參數傳遞同一鎖實例,而非依賴字符串常量或 static 字段。

掌握以上核心概念與最佳實踐,是編寫高質量 Java 併發程序的必經之路。

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