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

Java 進階教程:多線程編程核心機制與同步實戰

     編輯:Java 相關知識

多線程基礎概念

單線程 vs 多線程

  • 單線程:單一控制流,順序執行,像只有一個隊長的小隊
  • 多線程:多個控制流並發執行,每個線程有獨立棧,共享堆內存

線程實現層次

  • OS 級:內核線程,由操作系統調度,單核分時復用
  • 硬件級:多核、超線程,真正並行
  • JVM 級:Java 線程 1:1 映射 OS 線程 (主流實現)

創建線程的兩種方式

方式一:繼承 Thread 類


class MyThread extends Thread {
    private static int idGen = 0;
    
    public MyThread() {
        super("Thread-" + (++idGen)); // 設置線程名
    }
    
    @Override
    public void run() {
        System.out.println(getName() + " 正在運行");
    }
}

public class Test {
    public static void main(String[] args) {
        new MyThread().start(); // 必須調用 start(),不能調用 run()
        new MyThread().start();
    }
}

關鍵點:start() 啟動新線程並調用 run();直接調用 run() 只是主線程執行普通方法。

方式二:實現 Runnable 接口


class MyTask implements Runnable {
    @Override
    public void run() {
        System.out.println(Thread.currentThread().getName() + " 執行任務");
    }
}

public class Test {
    public static void main(String[] args) {
        Thread t1 = new Thread(new MyTask(), "Worker-1");
        Thread t2 = new Thread(new MyTask(), "Worker-2");
        t1.start(); t2.start();
    }
}

優勢:避免單繼承限制,任務與線程分離,配合線程池更靈活。

Thread 常用方法

方法說明
start()啟動線程
join()等待該線程終止
sleep(ms)當前線程休眠
setDaemon(true)設置守護線程 (JVM 退出不等待)
interrupt()中斷線程
currentThread()獲取當前線程

線程安全與同步機制

共享資源問題

多線程共享堆內存對象,並發修改導致競態條件:結果依賴執行時序。

synchronized 關鍵字

同步方法


class Counter {
    private int count = 0;
    
    public synchronized void increment() {  // 實例鎖:this
        count++;
    }
    
    public static synchronized void staticIncrement() {  // 類鎖:Counter.class
        // ...
    }
}

同步代碼塊 (關鍵區)


public void increment() {
    synchronized (this) {  // 也可用其他對象作為鎖
        count++;
    }
}

售票系統實戰案例


class TicketPool {  // 共享資源
    private int tickets = 100;
    
    public synchronized boolean sell() {  // 同步方法:原子化「判斷+出票」
        if (tickets > 0) {
            tickets--;
            System.out.println(Thread.currentThread().getName() + " 賣出第 " + (100-tickets) + " 張");
            return true;
        }
        return false;
    }
}

class Seller extends Thread {
    private TicketPool pool;
    private int sold = 0;
    
    public Seller(TicketPool pool) { this.pool = pool; }
    
    @Override
    public void run() {
        while (pool.sell()) {
            sold++;
            try { Thread.sleep((int)(Math.random() * 100)); } catch (InterruptedException e) {}
        }
        System.out.println(getName() + " 共賣出 " + sold + " 張");
    }
}

public class Test {
    public static void main(String[] args) {
        TicketPool pool = new TicketPool();
        new Seller(pool).start();
        new Seller(pool).start();
        new Seller(pool).start();
    }
}

輸出保證:無超賣、無重賣、總數正好 100 張。

對象鎖機制

  • 每個 Java 對象天然包含一把監視器鎖 (Monitor)
  • synchronized 方法/塊 = 獲取鎖 → 執行 → 釋放鎖
  • 可重入:同一線程可重複獲取同一鎖 (計數器機制)
  • 類鎖:static synchronized 鎖住 Class 對象

關鍵代碼塊優化


// 粗粒度鎖:整個方法同步,並發度低
public synchronized void process() { ... }

// 細粒度鎖:僅同步核心邏輯
public void process() {
    // 非共享操作...
    synchronized (sharedResource) {
        // 共享資源核心操作
    }
    // 非共享操作...
}

原則:同步塊越小越好,但不可破壞原子性邊界。

線程安全的三大特性

特性含義synchronized 支持
原子性操作不可分割✓ (鎖保證)
可見性修改對其他線程立即可見✓ (刷新主內存)
有序性禁止指令重排序✓ (happens-before)

常見陷阱與避坑

  1. 鎖對象變化:synchronized (new Object()) 每次新對象,失效
  2. 字符串常量鎖:synchronized ("lock") 常量池共享,意外阻塞
  3. 死鎖:多鎖獲取順序不一致 → 統一加鎖順序、嘗試鎖超時
  4. 過度同步:在同步塊調用異步/耗時操作 → 移出同步塊
  5. 忽略復合操作:if (!map.containsKey(k)) map.put(k,v) 需整體同步

現代替代方案 (JDK 5+)

  • Lock 接口:ReentrantLock、ReadWriteLock,支持公平鎖、可中斷、超時
  • 並發集合:ConcurrentHashMap、CopyOnWriteArrayList、BlockingQueue
  • 原子類:AtomicInteger、LongAdder,CAS 無鎖
  • 線程池:ExecutorService,統一管理線程生命周期
  • CompletableFuture:異步編程組合

總結

多線程核心在於共享資源的協調。synchronized 是 Java 最基礎的同步機制,通過對象監視器實現互斥訪問。理解「線程 vs 任務」、「鎖對象選擇」、「同步粒度」、「happens-before 規則」是寫出正確高效併發代碼的基石。現代開發應優先使用高級併發工具,但 synchronized 仍是底層基石,且在 JVM 優化 (偏向鎖、輕量級鎖、重量級鎖升級) 下性能優異。

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