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

Java 線程同步核心:共享物件鎖與 static 鎖的兩種實現模式

     編輯:Java 相關知識

同步本質:多線程競爭同一把鎖

Java 線程同步的核心是多線程共享同一個鎖物件。本文展示兩種構建共享鎖的經典模式:外部傳遞模式與靜態成員模式。

模式一:外部建立共享鎖,建構子注入

適用場景

  • 鎖的生命週期超過單一類
  • 需動態決定共享範圍(如模組級、應用級)
  • 單元測試易於注入 Mock 鎖

完整實現

// 任務類:實現 Runnable,接收外部鎖
class SharedLockTask implements Runnable {
    private final int threadId;
    private final Object lock; // 共享鎖引用
    
    public SharedLockTask(int id, Object lock) {
        this.threadId = id;
        this.lock = lock;
    }
    
    @Override
    public void run() {
        synchronized (lock) { // 競爭同一把鎖
            for (int i = 0; i < 100; i++) {
                System.out.println("Thread ID: " + threadId + " : " + i);
            }
        }
    }
}

// 啟動類
public class ExternalLockDemo {
    public static void main(String[] args) throws InterruptedException {
        Object sharedLock = new Object(); // 單一鎖物件
        
        for (int i = 0; i < 10; i++) {
            new Thread(new SharedLockTask(i, sharedLock)).start();
            Thread.sleep(1); // 錯峰啟動,觀察輸出順序
        }
    }
}

執行結果特徵

每個線程完整印出 0-99,無交叉輸出,證明同步生效。

模式二:static 靜態成員鎖,類級共享

適用場景

  • 鎖與類強綁定,天然單例
  • 無需外部管理鎖物件生命週期
  • 所有實例自動共享(含繼承類)

完整實現

class StaticLockTask implements Runnable {
    private final int threadId;
    // 類載入時初始化,所有實例共享
    private static final Object LOCK = new Object();
    
    public StaticLockTask(int id) {
        this.threadId = id;
    }
    
    @Override
    public void run() {
        synchronized (LOCK) {
            for (int i = 0; i < 100; i++) {
                System.out.println("Thread ID: " + threadId + " : " + i);
            }
        }
    }
}

public class StaticLockDemo {
    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 10; i++) {
            new Thread(new StaticLockTask(i)).start(); // 無需傳遞鎖
            Thread.sleep(1);
        }
    }
}

深度對比:選哪個?

維度外部傳遞鎖static 靜態鎖
靈活性高(可注入不同鎖實現隔離)低(硬編碼類級共享)
封裝性較差(鎖洩漏到建構子參數)優(鎖為私有實現細節)
繼承行為子類需顯式傳遞子類自動共享父類鎖
測試友善度極佳(可注入測試專用鎖)需反射或 PowerMock 修改
序列化影響無static 不序列化,反序列化後仍共享
類載入器隔離不受影響不同 ClassLoader 載入會有多份鎖

進階陷阱:this、Class 物件與字串常量

錯誤示範

// 1. synchronized(this) —— 只有同一實例才同步
synchronized (this) { ... } // 不同實例互不阻塞!

// 2. synchronized(getClass()) —— 繼承類會有不同 Class 物件
synchronized (getClass()) { ... } // 子類實例不阻塞父類實例!

// 3. synchronized("lock") —— 字串常量池共享,全 JVM 可見
synchronized ("global") { ... } // 極度危險:非預期跨模組阻塞

正確選擇

// 實例級鎖:私有 final 物件
private final Object instanceLock = new Object();

// 類級鎖:私有 static final 物件
private static final Object classLock = new Object();

// 方法級同步(隱含 this/class 鎖,慎用)
public synchronized void instanceMethod() { }
public static synchronized void staticMethod() { }

現代最佳實踐:Lock 介面

// 實例級
private final Lock lock = new ReentrantLock();

// 類級
private static final Lock lock = new ReentrantLock();

// 使用
lock.lock();
try {
    // 臨界區
} finally {
    lock.unlock(); // 必須在 finally 中釋放
}

優勢:可中斷、超時、公平性、多條件變數、非區塊結構鎖定。

總結

共享鎖是 Java 同步的靈魂。外部傳遞模式適合需靈活控制共享範圍的框架代碼;static 靜態鎖適合業務邏輯內部同步。切記:鎖物件必須 final、不可變、私有,避免 this、getClass()、字串常量等「隱性共享」陷阱。現代專案優先考慮 ReentrantLock 或 java.util.concurrent 高層工具。

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