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

Java 同步機制真相:synchronized 鎖的是物件而非代碼,關鍵區段實現需鎖定共享物件

     編輯:Java 相關知識

核心誤區

許多開發者誤以為 synchronized 鎖住的是「代碼塊」,實則 鎖的是物件。只要持有同一把鎖 (物件),才能互斥;不同實例各有鎖,同步方法可並發執行。

反例證明:非靜態同步方法可並發

class Foo extends Thread {
    private int val;
    public Foo(int v) { val = v; }

    public synchronized void printVal(int v) { // 鎖住 this (Foo 實例)
        while (true) System.out.println(v);
    }
    public void run() { printVal(val); }
}

public class SyncTest {
    public static void main(String[] args) {
        Foo f1 = new Foo(1); f1.start(); // 鎖 f1
        Foo f2 = new Foo(3); f2.start(); // 鎖 f2 (不同鎖!)
    }
}

輸出:1 和 3 交替輸出 → 兩線程同時執行 printVal!

若 synchronized 鎖代碼,輸出只能是全 1 或全 3。

正確實現關鍵區段:鎖定共享物件

方法一:鎖定 Class 物件 (類級別鎖)

public void printVal(int v) {
    synchronized (Foo.class) { // 所有實例共享唯一 Class 物件
        while (true) System.out.println(v);
    }
}
  • Foo.class 在 JVM 中唯一,所有實例競爭同一把鎖
  • 效果等同 public static synchronized void printVal(...)

方法二:鎖定專用靜態物件 (推薦,粒度更細)

class Foo extends Thread {
    private int val;
    private static final Object LOCK = new Object(); // 專用鎖物件

    public Foo(int v) { val = v; }

    public void printVal(int v) {
        synchronized (LOCK) { // 明確語義、不暴露類鎖
            while (true) System.out.println(v);
        }
    }
    public void run() { printVal(val); }
}
  • 優點:封裝性好、不干擾其他同步邏輯、可獨立演進
  • 原則:盡量縮小同步粒度,提升並發度

方法三:鎖定共享可變物件 (如 String 常數池特性)

class Foo extends Thread {
    private String name, val;
    public Foo(String name, String v) { this.name = name; val = v; }

    public void printVal() {
        synchronized (val) { // 同樣字串常數池引用同一物件
            while (true) System.out.println(name + val);
        }
    }
    public void run() { printVal(); }
}

public class SyncMethodTest {
    public static void main(String[] args) {
        // 關鍵:傳入同一字串常數 "printVal" → 共享同一鎖物件
        Foo f1 = new Foo("Foo 1:", "printVal"); f1.start();
        Foo f2 = new Foo("Foo 2:", "printVal"); f2.start();
    }
}

注意:字串常數池導致 "printVal" 為同一物件;若用 new String("printVal") 則為不同物件,同步失效。

總結對比表

同步寫法鎖物件保護範圍適用場景
synchronized void method()this (實例)該實例所有同步方法實例狀態保護
static synchronized void method()Class 物件該類所有靜態同步方法靜態狀態保護
synchronized(lock) { }lock 物件同一 lock 的所有區塊精細控制、跨方法/類協調
synchronized(Foo.class) { }Foo.class同類所有此類區塊類級別協調

最佳實踐原則

  1. 私有最終鎖物件:private final Object lock = new Object(); 封裝鎖,防外部干擾
  2. 避免鎖公開物件:this、Class、字串常數、集合等可能被外部代碼鎖住導致死鎖/活鎖
  3. 縮小同步範圍:僅保護共享狀態讀寫,計算/IO 移出同步塊
  4. 優先用 java.util.concurrent:ReentrantLock、ReadWriteLock、ConcurrentHashMap、Atomic* 等高階工具
  5. 不可變物件:無狀態共享,根本不需同步
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved