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

Java 多線程基礎總結(一):synchronized 核心機制與死鎖陷阱

     編輯:Java 相關知識

Java 多線程的基石是 **對象隱式鎖(Monitor)**,synchronized 是使用它的唯一語法。本文從底層原理切入,揭示 wait/notify 必須在同步塊中的原因,並實戰分析兩種經典死鎖場景。

為什麼 wait/notify 必須在 synchronized 內?

  • 無鎖時,多線程可同時進入方法,wait() 會將**所有**線程放入等待集,卻無線程持有鎖去 notify() → 永久阻塞。
  • synchronized 保證**同一時刻僅一線程持有鎖**,該線程 wait() 時**釋放鎖**,其它線程才能進入並喚醒它。
  • 每個對象獨立管理等待集,避免跨對象干擾;單鎖模式降低死鎖概率。

synchronized 三大固有缺陷

  1. **無法中斷**正在等待鎖的線程。
  2. **無法設置超時**獲取鎖。
  3. **單一條件**限制(JDK 5 ReentrantLock+Condition 已解決)。

經典死鎖案例一:鎖順序不一致

class DeadLockSample {
    public final Object lock1 = new Object();
    public final Object lock2 = new Object();

    public void methodOne() {
        synchronized (lock1) {
            synchronized (lock2) { /* ... */ }
        }
    }
    public void methodTwo() {
        synchronized (lock2) {
            synchronized (lock1) { /* ... */ }
        }
    }
}

線程 A 持有 lock1 等 lock2,線程 B 持有 lock2 等 lock1 → **循環等待,死鎖**。

經典死鎖案例二:wait() 釋放鎖不完全

class WaitAndNotify {
    final List<String> list = new LinkedList<>();

    public synchronized void push(String x) {
        synchronized (list) { list.add(x); notify(); }
    }
    public synchronized Object pop() {
        synchronized (list) {
            if (list.isEmpty()) {
                try { wait(); } catch (InterruptedException e) { e.printStackTrace(); }
            }
            return list.size();
        }
    }
}

**死鎖過程**:

    1. 線程 T1 進入 pop(),持有 this 鎖與 list 鎖,wait() 釋放 this 鎖,**但仍持有 list 鎖**。 2. 線程 T2 獲取 this 鎖進入 push(),嘗試獲取 list 鎖 → **永久阻塞**。 3. T1 等待 notify(),T2 等待 list 鎖 → **雙方僵持**。

根因:wait() 僅釋放**當前對象**的鎖,無法像棧桢式釋放所有嵌套鎖。

總結

  • synchronized 是進入並發世界的大門,但並非萬能鑰匙。
  • 理解 **鎖對象、等待集、釋放範圍** 是避免死鎖的關鍵。
  • 下一篇將講解 Thread 的 daemon 與 join 協作機制。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved