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

Java 線程同步機制全解析:synchronized、wait/notify、Lock/Condition 核心原理與最佳實踐

     編輯:Java 程式設計入門

為什麼需要同步?

多線程並發訪問共享可變數據時,會出現原子性違背、可見性失效、有序性破壞,導致數據不一致。同步機制通過互斥鎖與內存屏障保證:

  • 同一時刻僅一線程進入臨界區(互斥);
  • 鎖釋放前的寫入對後續獲取鎖的線程可見(內存屏障)。

兩種同步基礎形式

1. 同步代碼塊

synchronized(lockObject) {
    // 臨界區:操作共享數據
}
  • lockObject 必須為同一對象(通常用共享數據本身、this、類對象、專用鎖對象);
  • 鎖粒度可控,性能優於同步方法。

2. 同步方法

public synchronized void method() { ... }        // 鎖 this
public static synchronized void staticMethod() { ... } // 鎖 Class 對象
  • 隱式使用 this 或 Class 作為鎖;
  • 粒度較粗,容易成為併發瓶頸。

線程通信:wait/notify/notifyAll

屬於 Object 方法,必須在同步塊/方法內調用,否則拋 IllegalMonitorStateException。

方法作用關鍵點
wait()釋放鎖,進入等待集合,阻塞當前線程可被 notify/notifyAll 或中斷喚醒
wait(long timeout)超時自動喚醒超時返回不拋異常,需重新檢查條件
notify()喚醒任一等待線程喚醒線程需重新競爭鎖
notifyAll()喚醒所有等待線程推薦使用,避免信號丟失導致死鎖

經典生產者-消費者模板

class Buffer {
    private final List<Integer> queue = new ArrayList<>();
    private final int capacity;

    public synchronized void put(int v) throws InterruptedException {
        while (queue.size() == capacity) wait(); // 滿等待
        queue.add(v);
        notifyAll(); // 喚醒消費者
    }

    public synchronized int take() throws InterruptedException {
        while (queue.isEmpty()) wait(); // 空等待
        int v = queue.remove(0);
        notifyAll(); // 喚醒生產者
        return v;
    }
}

進階:Lock 接口與 Condition(JUC)

ReentrantLock 提供比 synchronized 更豐富的功能:

  • 可中斷獲取鎖:lockInterruptibly();
  • 公平鎖選項:new ReentrantLock(true);
  • 多條件變量:lock.newCondition() 區分不同等待隊列(如 notEmpty/notFull),精準喚醒;
  • 嘗試非阻塞獲取:tryLock()/tryLock(timeout, unit)。

ArrayBlockingQueue 核心同步邏輯(JUC 典範)

final ReentrantLock lock = new ReentrantLock();
final Condition notEmpty = lock.newCondition();
final Condition notFull  = lock.newCondition();

// 入隊
lock.lock();
try {
    while (count == items.length) notFull.await();
    insert(x);
    notEmpty.signal();
} finally { lock.unlock(); }

// 出隊
lock.lock();
try {
    while (count == 0) notEmpty.await();
    return extract();
    notFull.signal();
} finally { lock.unlock(); }

同步最佳實踐原則

  1. 縮小鎖範圍:僅同步訪問共享狀態的代碼,避免 I/O、睡眠在鎖內;
  2. 優先用 JUC 並發容器:ConcurrentHashMap、CopyOnWriteArrayList、BlockingQueue 等無鎖/細粒度鎖實現;
  3. 不可變對象:final 字段、String、LocalDate 天然線程安全,無需同步;
  4. ThreadLocal 隔離:線程私有副本避免共享;
  5. 避免死鎖:固定鎖獲取順序、使用 tryLock 超時退出、減少嵌套鎖;
  6. volatile 不能替代同步:僅保證可見性,不保證原子性(如 i++ 仍需同步)。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved