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

Java 多線程同步進階:synchronized 代碼塊、鎖對象、靜態鎖與內部類鎖機制深度解析

     編輯:Java 綜合教學

同步代碼塊 vs 同步方法:粒度與性能權衡

synchronized 關鍵字既可修飾方法,也可作為代碼塊使用。同步代碼塊允許精確控制鎖範圍,將非臨界區代碼移出同步塊,顯著提升並發吞吐。

// 同步方法:整個方法加鎖,耗時 6 秒
public synchronized void doLongTask() {
    Thread.sleep(3000); // 非臨界區也被鎖住
    // 臨界區操作
}

// 同步代碼塊:僅臨界區加鎖,耗時 3 秒
public void doLongTask() {
    Thread.sleep(3000); // 異步執行,無鎖
    synchronized(this) { // 僅共享數據賦值加鎖
        this.data1 = result1;
        this.data2 = result2;
    }
}

同步代碼塊間的互斥關係

同一對象的多個 synchronized(this) 代碼塊互斥,共享同一對象監視器。不同對象的同步塊不互斥。

public class Service {
    public void methodA() {
        synchronized(this) { sleep(2000); } // 鎖 this
    }
    public void methodB() {
        synchronized(this) { /* 邏輯 */ } // 鎖 this,與 A 互斥
    }
}

靜態同步方法與 Class 鎖

  • synchronized static void method():鎖 ClassName.class 對象
  • synchronized(ClassName.class) { }:等效代碼塊寫法
  • 實例鎖(this)與類鎖(Class)互不干擾
public class Demo {
    synchronized static void printA() { sleep(3000); } // 鎖 Demo.class
    synchronized static void printB() { sleep(3000); } // 鎖 Demo.class,與 A 互斥
    synchronized void printC() { sleep(3000); } // 鎖 this,與 A/B 不互斥
}

內部類與外部類鎖獨立性

內部類實例擁有獨立的 this 引用,其同步鎖與外部類實例鎖完全無關。

public class Outer {
    static class Inner1 {
        public void method1(Inner2 i2) {
            synchronized(i2) { /* 鎖 Inner2 實例 */ }
        }
        public synchronized void method2() { /* 鎖 Inner1 實例 */ }
    }
    static class Inner2 {
        public synchronized void method1() { /* 鎖 Inner2 實例 */ }
    }
}
// Inner1.method1(inner2) 與 Inner2.method1() 互斥(同鎖 inner2)
// Inner1.method2() 與上述兩者不互斥(不同鎖)

鎖對象變更的致命陷阱

同步塊中修改鎖引用會導致後續線程獲取新鎖,破壞互斥:

private String lock = "123";
public void flawed() {
    synchronized(lock) { // 線程 A 獲取 "123" 鎖
        lock = "456";    // 修改引用!
        sleep(2000);
    }
}
// 線程 B 可能獲取 "456" 鎖,與 A 並發執行!

正確模式:final 鎖對象 / 私有不可變對象

// 推薦 1:final 引用指向不可變對象
private final Object lock = new Object();
private final byte[] lockBytes = new byte[0]; // 零開銷鎖對象

// 推薦 2:業務對象作為鎖(生命週期固定)
public void transfer(Account from, Account to) {
    Account first = from.hashCode() < to.hashCode() ? from : to; // 固定獲取順序防死鎖
    Account second = first == from ? to : from;
    synchronized(first) {
        synchronized(second) { /* 轉賬邏輯 */ }
    }
}

最佳實踐總結

  1. 優先使用 synchronized(lock) 代碼塊,縮小鎖範圍
  2. 鎖對象聲明 final,不可變,不可為基本類型包裝類
  3. 靜態方法鎖 Class.class,實例方法鎖 this,兩者隔離
  4. 多鎖場景固定獲取順序(hashCode/ID),防死鎖
  5. 高併發場景考慮 ReentrantLock、LongAdder、ConcurrentHashMap 替代
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved