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

Java 多線程同步深度解析:synchronized 方法、同步塊、this 與 object 鎖的本質區別

     編輯:Java 綜合教學

Java 多線程同步深度解析:synchronized 方法、同步塊、this 與 object 鎖的本質區別

synchronized 兩種基本形式

1. 同步方法

public synchronized void accessVal(int newVal) { ... }
public static synchronized void staticMethod() { ... }
  • 實例方法:鎖對象為 this (當前實例)
  • 靜態方法:鎖對象為 ClassName.class (類對象)
  • 同一實例的所有同步方法共用一把鎖,同一時刻只能有一個線程執行任一同步方法

2. 同步塊

synchronized(syncObject) {
    // 受保護代碼
}
  • syncObject 可為任意物件:this、其他實例、ClassName.class、字串常量、專用鎖物件
  • 粒度更細,只鎖住關鍵代碼段,提升並發效能
  • 靈活指定鎖對象,實現複雜同步協作

核心誤區與正解

誤區一:synchronized 只能用在 Thread/Runnable 相關類中

正解:synchronized 可用於任何類的方法/塊中。只要有多線程並發訪問共享資源,即需同步,與類是否繼承 Thread 或實現 Runnable 無關。

誤區二:synchronized(this) 與 synchronized(object) 作用範圍完全不同

正解:本質相同,鎖的是括號內的物件。差別在於鎖對象的選擇策略:

場景推薦鎖對象說明
同步實例方法/塊,保護實例變量this當前實例唯一,天然適合
同步靜態方法/塊,保護靜態變量MyClass.class類對象全局唯一
多個實例需協同同步共享的 Object lock = new Object()顯式鎖對象,語義清晰
調用其他物件的同步方法被調用物件實例synchronized(otherObj) { otherObj.method(); }

實戰代碼演示

共享資源類

public class SharedResource {
    public void methodA() {
        // synchronized(this) {  // 等價於方法加 synchronized
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " A i=" + i);
        }
        // }
    }
    public void methodB() {
        // synchronized(this) {
        for (int i = 0; i < 100; i++) {
            System.out.println(Thread.currentThread().getName() + " B i=" + i);
        }
        // }
    }
}

線程類:傳入共享資源,用資源實例作鎖

public class WorkerA implements Runnable {
    private final SharedResource resource;
    public WorkerA(SharedResource resource) { this.resource = resource; }
    @Override
    public void run() {
        synchronized (resource) { // 鎖住 resource 實例
            resource.methodA();
        }
    }
}

public class WorkerB implements Runnable {
    private final SharedResource resource;
    public WorkerB(SharedResource resource) { this.resource = resource; }
    @Override
    public void run() {
        synchronized (resource) { // 同一把鎖
            resource.methodB();
        }
    }
}

主程序啟動

public class Main {
    public static void main(String[] args) {
        SharedResource res = new SharedResource();
        new Thread(new WorkerA(res), "Thread-A").start();
        new Thread(new WorkerB(res), "Thread-B").start();
    }
}

執行結果分析

輸出將顯示 Thread-A 完整執行 100 次 methodA,隨後 Thread-B 執行 methodB(或相反順序),不會交叉輸出。因為兩線程競爭同一把鎖 (res 實例)。

進階:避免常見同步陷阱

  • 鎖字串常量:synchronized("lock") 危險!字串常量池全局共享,可能導致無關代碼意外互斥
  • 鎖基本型別包裝類:synchronized(Integer.valueOf(1)) 同理,緩存池導致共享
  • 鎖 this 但方法被重寫:子類重寫同步方法時,鎖仍是 this (子類實例),正常工作
  • 死鎖預防:固定鎖獲取順序、使用 tryLock(timeout) (ReentrantLock)、避免巢狀同步

效能建議

  1. 優先使用同步塊縮小鎖範圍
  2. 高併發場景考慮 ReentrantLock、ReadWriteLock、StampedLock、ConcurrentHashMap 等 JUC 組件
  3. 無狀態設計、ThreadLocal、不可變物件從根本減少同步需求
  4. JVM 鎖優化:偏向鎖 → 輕量級鎖 → 重量級鎖 (JDK 6+ 自動升級)

理解 synchronized 的鎖對象語義,是編寫正確、高效並發程序的基石。

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