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

Java 鎖機制核心原理與代碼實戰解析

     編輯:Java 相關知識

前言

在 Java 多線程編程中,鎖機制是實現線程同步、保證數據一致性的基石。本文將通過三個經典代碼示例,深入淺出地講解 Java 中 synchronized 關鍵字的使用方式、對象鎖與類鎖的區別,以及鎖的釋放時機。

一、無鎖狀態下的線程競爭

首先看一個沒有任何同步措施的例子,多個線程並發訪問共享資源時會發生什麼:

public class ThreadTest extends Thread {
    private int threadNo;

    public ThreadTest(int threadNo) {
        this.threadNo = threadNo;
    }

    public static void main(String[] args) {
        for (int i = 0; i < 5; i++) {
            new ThreadTest(i).start();
        }
    }

    @Override
    public void run() {
        for (int i = 0; i < 5; i++) {
            System.out.println("Thread-" + threadNo + ": " + i);
        }
    }
}

現象:多個線程的輸出順序完全不可預期,交替輸出,甚至出現數據混亂。這就是典型的「線程不安全」。

二、使用 synchronized 代碼塊實現同步

通過引入一把「鎖對象」,確保同一時刻只有一個線程能執行關鍵代碼區:

public class ThreadTest2 extends Thread {
    private int threadNo;
    private String lock;

    public ThreadTest2(int threadNo, String lock) {
        this.threadNo = threadNo;
        this.lock = lock;
    }

    public static void main(String[] args) {
        String lock = new String(); // 共享鎖對象
        for (int i = 0; i < 5; i++) {
            new ThreadTest2(i, lock).start();
        }
    }

    @Override
    public void run() {
        synchronized (lock) { // 獲取鎖
            for (int i = 0; i < 5; i++) {
                System.out.println("Thread-" + threadNo + ": " + i);
            }
        } // 釋放鎖
    }
}

關鍵點:

  • synchronized(lock) 要求所有線程使用同一個鎖對象實例。
  • 鎖住的是代碼塊,而非方法整體,粒度更細,並發性能更好。
  • 線程進入代碼塊前必須獲取鎖,退出(正常或異常)自動釋放鎖。

三、同步方法與類鎖的陷阱

很多開發者誤以為在方法上加 synchronized 就能解決所有問題,但這裡有個大坑:

public class ThreadTest3 extends Thread {
    private int threadNo;

    public ThreadTest3(int threadNo) {
        this.threadNo = threadNo;
    }

    public static void main(String[] args) throws InterruptedException {
        for (int i = 0; i < 5; i++) {
            new ThreadTest3(i).start();
            Thread.sleep(10); // 嘗試錯開啟動時間
        }
    }

    // 這是一個實例方法,鎖的是 this 對象
    public synchronized void abc(int threadNo) {
        for (int i = 0; i < 5; i++) {
            System.out.println("Thread-" + threadNo + ": " + i);
        }
    }

    @Override
    public void run() {
        abc(threadNo);
    }
}

為什麼仍然無效?

每次 new ThreadTest3(i) 都創建了一個新的對象實例,每個線程持有的 this 都不一樣,所以鎖的是不同的對象,根本起不到互斥作用!

四、正確的類鎖寫法

如果想讓所有實例共享一把鎖,必須鎖住 Class 對象:

public static synchronized void staticSyncMethod() {
    // 鎖的是 ThreadTest3.class
}

public void instanceSyncMethod() {
    synchronized (ThreadTest3.class) {
        // 同樣鎖住類對象
    }
}

五、總結與最佳實踐

鎖類型鎖對象適用場景注意事項
對象鎖this / 自定義對象實例方法、代碼塊必須確保多線程共享同一實例
類鎖ClassName.class靜態方法、跨實例同步粒度較大,影響並發度

核心原則:

  1. 共享性:多線程必須訪問同一把鎖。
  2. 最小範圍:優先使用同步代碼塊,縮小鎖粒度。
  3. 避免死鎖:按固定順序獲取多把鎖,使用 tryLock 超時機制。

掌握鎖機制的本質,是寫出高性能、線程安全 Java 代碼的關鍵。

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