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

Java 同步機制深度對比:synchronized vs Lock 全方位解析

     編輯:Java 程式設計入門

Java 併發編程中,synchronized 與 java.util.concurrent.locks.Lock 是兩大核心同步手段。它們功能重疊但設計哲學、性能表現、使用方式截然不同。

核心異同速覽

維度synchronizedLock (ReentrantLock)
本質JVM 內置關鍵字JDK API 級別接口
鎖釋放自動(塊結束/異常拋出)手動 unlock(),必須在 finally 中調用
性能JDK 1.6+ 優化後接近 Lock高併發下優勢明顯
公平性非公平(不可配置)可選公平/非公平
中斷響應不支持lockInterruptibly() 支持
超時等待不支持tryLock(timeout) 支持
條件變量wait/notify 綁定對象鎖Condition 可多條件精準控制

synchronized 核心機制

1. 對象鎖與重入計數

  • 每個對象天然持有一把監視器鎖
  • JVM 維護鎖計數器:首次獲取計數=1,同線程重入計數+1,退出同步塊計數-1,歸零釋放鎖
  • 只有持鎖線程可重入,其他線程阻塞

2. 同步塊鎖定目標

// 鎖定當前實例
synchronized (this) { ... }

// 鎖定類對象(等同 static synchronized 方法)
synchronized (MyClass.class) { ... }

// 鎖定任意對象
synchronized (sharedObject) { ... }

經典陷阱:static 字段與實例鎖不匹配

class Test {
    public static User user = null;  // 靜態字段,屬於類

    // 實例方法鎖定 this,多個實例互不阻塞!
    public synchronized void add(User u) {
        user = u;
        Dao.save(user);  // 競態條件:線程1尚未save,線程2已修改user
    }
}

兩個線程分別 new Test(),調用 add() 時鎖的是不同實例,無法同步訪問 static user。

修正方案一:static synchronized

public static synchronized void add(User u) { ... }  // 鎖定 Test.class

修正方案二:Lock 實現跨實例同步


class Test {
    public static User user = null;
    private static final Lock lock = new ReentrantLock();

    public void add(User u) {
        lock.lock();
        try {
            user = u;
            Dao.save(user);
        } finally {
            lock.unlock();  // 必須釋放!
        }
    }
}

ReentrantLock 為靜態字段,所有實例共享同一把鎖,等效於 static synchronized,但提供更精細控制(可中斷、超時、公平鎖、多 Condition)。

最佳實踐建議

  1. 優先使用 synchronized:代碼簡潔、JVM 原生優化(偏向鎖、輕量級鎖、鎖消除、鎖粗化),維護成本低
  2. 需要高級特性時用 Lock:公平鎖、可中斷獲取、超時等待、多條件變量、非塊結構鎖定
  3. Lock 必須配合 try-finally:防止異常導致死鎖
  4. 避免鎖粒度過粗:縮小同步塊範圍,降低爭用

總結

synchronized 是「開箱即用」的同步利器,現代 JVM 已極度優化;Lock 是「專業級工具箱」,解決 synchronized 無法覆蓋的進階場景。理解雙方本質,按需選型,才是併發編程的正道。

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