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

Java 線程同步鎖選擇指南:避免 String 常量池導致的誤同步陷阱

     編輯:Java 程式設計入門

synchronized 鎖對象的選擇直接決定同步範圍。錯誤選擇(如 String 常量、Boxed 基本類型)會導致非預期的跨實例同步,嚴重影響併發性能甚至死鎖。

錯誤示範:String 字面量鎖

public class SyncTest {
    private String name = "name"; // 字面量進入常量池
    public void method(String flag) {
        synchronized (name) { // 所有實例共用同一把鎖!
            System.out.println(flag + ", invoke method ....");
            Thread.sleep(1000);
        }
    }
    public static void main(String[] args) {
        SyncTest t1 = new SyncTest(), t2 = new SyncTest();
        // 4 條線程分別綁定 t1、t1、t2、t2
        // 預期:t1 兩線程同步,t2 兩線程同步,t1/t2 互不干擾
        // 實際:4 線程全串行,因鎖定同一常量池 "name"
    }
}

推薦做法:專用零長度 byte 數組

public class SyncTest {
    private final byte[] lock = new byte[0]; // 每個實例唯一、不可變、零內存
    public void method(String flag) {
        synchronized (lock) { // 實例級鎖,互不干擾
            System.out.println(flag + ", invoke method f....");
            Thread.sleep(1000);
        }
    }
    // main 測試同前,輸出符合預期:t1 兩線程串行,t2 兩線程串行,t1/t2 並行
}

為什麼選 new byte[0]?

候選對象問題推薦度
String 字面量常量池共享,跨實例誤同步❌
new String()每次 new 創建新對象,若非 final 字段可能被重新賦值導致鎖失效⚠️
Integer/Long 等包裝類-128~127 緩存導致共享;大數值 new 出新對象同 String 問題❌
this暴露鎖給外部調用者,破壞封裝,易死鎖⚠️
private final Object lock = new Object()標準做法,每實例 ~16 字節頭部開銷✅
private final byte[] lock = new byte[0]零數組無數據區,僅對象頭 ~16 字節,語義明確「僅作鎖」⭐ 最佳

進階:ReentrantLock 替代方案

需可中斷、公平、多條件變量時,優先 java.util.concurrent.locks.ReentrantLock:

private final Lock lock = new ReentrantLock();
public void method() {
    lock.lock();
    try { /* critical section */ }
    finally { lock.unlock(); }
}

總結

  1. 鎖對象必須 final、不可變、實例私有。
  2. 首選 private final byte[] lock = new byte[0],零內存、語義清晰。
  3. 避免 String、包裝類、this 等「共享/可變/公開」對象。
  4. 複雜同步需求轉用 Lock 體系。
  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved