Synchronized 底層原理與 Lock 的核心區(qū)別
前言
圖片
在Java并發(fā)編程中,synchronized和Lock是實(shí)現(xiàn)線程同步的兩大核心機(jī)制。理解它們的底層原理和核心區(qū)別,對(duì)于編寫高效、安全的并發(fā)代碼至關(guān)重要。
synchronized 底層原理
對(duì)象頭與 Mark Word
在Java中,每個(gè)對(duì)象都有一個(gè) 對(duì)象頭(Object Header),其中包含Mark Word和類型指針。Mark Word是實(shí)現(xiàn)synchronized的關(guān)鍵,它存儲(chǔ)了對(duì)象的鎖狀態(tài)、HashCode、GC年齡等信息。
不同鎖狀態(tài)下,Mark Word 的結(jié)構(gòu)不同:
- 無(wú)鎖狀態(tài):存儲(chǔ)對(duì)象的HashCode和GC年齡。
 - 偏向鎖狀態(tài):存儲(chǔ)偏向線程ID,表明該對(duì)象長(zhǎng)期被某一線程獨(dú)占。
 - 輕量級(jí)鎖狀態(tài):存儲(chǔ)指向線程棧中鎖記錄的指針。
 - 重量級(jí)鎖狀態(tài):存儲(chǔ)指向Monitor鎖的指針。
 
Monitor 鎖機(jī)制
當(dāng)synchronized升級(jí)到重量級(jí)鎖時(shí),會(huì)依賴Monitor鎖(也稱為管程)。Monitor 是一種同步工具,本質(zhì)是操作系統(tǒng)提供的互斥鎖(Mutex),通過控制線程的阻塞與喚醒實(shí)現(xiàn)同步。
Monitor的核心結(jié)構(gòu)包括:
- Owner:指向當(dāng)前持有鎖的線程。
 - EntryList:存儲(chǔ)等待獲取鎖的線程隊(duì)列。
 - WaitSet:存儲(chǔ)調(diào)用wait()方法后釋放鎖的線程隊(duì)列。
 
線程獲取Monitor鎖的流程:
- 線程嘗試獲取鎖,若Owner為空,則直接占有鎖并將Owner指向自己。
 - 若鎖已被其他線程占有,則當(dāng)前線程進(jìn)入EntryList阻塞等待。
 - 當(dāng)Owner線程釋放鎖時(shí),會(huì)喚醒EntryList中的線程重新競(jìng)爭(zhēng)鎖。
 
鎖升級(jí)策略
為減少鎖競(jìng)爭(zhēng)帶來的性能損耗,JVM引入了 鎖升級(jí) 機(jī)制,從低開銷的鎖狀態(tài)逐步升級(jí)到高開銷狀態(tài):
- 偏向鎖:適用于單線程重復(fù)獲取鎖的場(chǎng)景。線程首次獲取鎖時(shí),Mark Word記錄線程ID,后續(xù)該線程可直接獲取鎖,無(wú)需競(jìng)爭(zhēng)。
 - 輕量級(jí)鎖:當(dāng)有其他線程競(jìng)爭(zhēng)鎖時(shí),偏向鎖升級(jí)為輕量級(jí)鎖。線程通過CAS操作將Mark Word中的鎖記錄指針指向自己的棧幀,避免進(jìn)入內(nèi)核態(tài)阻塞。
 - 重量級(jí)鎖:若輕量級(jí)鎖競(jìng)爭(zhēng)激烈(多線程頻繁CAS失?。?,則升級(jí)為重量級(jí)鎖。此時(shí)依賴Monitor鎖,線程會(huì)進(jìn)入內(nèi)核態(tài)阻塞,性能開銷較大。
 
Lock 的核心實(shí)現(xiàn)
Lock是Java 5引入的接口(位于java.util.concurrent.locks包),通過API層面實(shí)現(xiàn)線程同步,其核心實(shí)現(xiàn)類為ReentrantLock。Lock的底層依賴 AQS(AbstractQueuedSynchronizer)框架。
AQS 核心原理
AQS是并發(fā)工具的基礎(chǔ)框架,通過同步狀態(tài)和雙向阻塞隊(duì)列實(shí)現(xiàn)鎖的獲取與釋放:
- 同步狀態(tài)(state):用volatile int變量存儲(chǔ)鎖的狀態(tài)(0表示未鎖定,大于0表示已鎖定,支持可重入)。
 - 雙向阻塞隊(duì)列:存儲(chǔ)等待獲取鎖的線程,通過FIFO原則保證線程公平性(可選)。
 
線程獲取鎖的流程(以 ReentrantLock 為例):
- 調(diào)用lock()方法時(shí),線程嘗試通過CAS操作修改state:
 
若state=0,修改為1并成功獲取鎖。
若state>0且當(dāng)前線程是鎖持有者(可重入),則state加1。
若獲取失敗,線程進(jìn)入雙向隊(duì)列阻塞等待。
- 調(diào)用unlock()方法時(shí),線程減少state值:
 若state減為0,釋放鎖并喚醒隊(duì)列中的線程。
Lock 的核心特性
Lock接口定義了比synchronized更靈活的同步操作,核心方法包括:
- lock():獲取鎖,若未獲取則阻塞。
 - lockInterruptibly():可中斷地獲取鎖,允許線程響應(yīng)中斷。
 - tryLock():嘗試非阻塞獲取鎖,成功返回true。
 - tryLock(long time, TimeUnit unit):超時(shí)獲取鎖。
 - unlock():釋放鎖(需手動(dòng)調(diào)用,通常配合try-finally)。
 - newCondition():創(chuàng)建條件變量,實(shí)現(xiàn)線程間的精細(xì)化通信。
 
synchronized 與 Lock 的核心區(qū)別
對(duì)比維度  | synchronized  | Lock  | 
實(shí)現(xiàn)層面  | JVM 內(nèi)置關(guān)鍵字,依賴底層字節(jié)碼指令(monitorenter/monitorexit)  | Java 類庫(kù)接口,基于 AQS 框架實(shí)現(xiàn)  | 
鎖的獲取與釋放  | 自動(dòng)獲取與釋放(由 JVM 控制)  | 手動(dòng)獲取與釋放(需顯式調(diào)用 lock()/unlock(),建議用 try-finally 確保釋放)  | 
可中斷性  | 不可中斷,一旦阻塞無(wú)法響應(yīng)中斷  | 可中斷(lockInterruptibly())  | 
超時(shí)獲取  | 不支持  | 支持(tryLock(long time, TimeUnit unit))  | 
公平性  | 非公平鎖(無(wú)法設(shè)置)  | 可選擇公平鎖或非公平鎖(構(gòu)造函數(shù)參數(shù)控制)  | 
條件變量  | 僅支持一個(gè)條件變量(通過 wait()/notify() 實(shí)現(xiàn))  | 支持多個(gè)條件變量(newCondition() 創(chuàng)建)  | 
性能  | 低競(jìng)爭(zhēng)場(chǎng)景下性能優(yōu)異(鎖升級(jí)優(yōu)化);高競(jìng)爭(zhēng)場(chǎng)景下略遜于 Lock  | 高競(jìng)爭(zhēng)場(chǎng)景下性能更穩(wěn)定(減少內(nèi)核態(tài)阻塞開銷)  | 
可重入性  | 支持(同一線程可重復(fù)獲取鎖)  | 支持(ReentrantLock 是可重入鎖)  | 
使用案例
synchronized 使用案例
同步方法:
public class SynchronizedMethodExample {
    private int count = 0;
    // 同步實(shí)例方法,鎖為當(dāng)前對(duì)象
    public synchronized void increment() {
        count++;
    }
    public int getCount() {
        return count;
    }
    public static void main(String[] args) throws InterruptedException {
        SynchronizedMethodExample example = new SynchronizedMethodExample();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("Count: " + example.getCount()); // 輸出 2000
    }
}同步代碼塊:
public class SynchronizedBlockExample {
    private int count = 0;
    private Object lock = new Object();
    public void increment() {
        // 同步代碼塊,鎖為lock對(duì)象
        synchronized (lock) {
            count++;
        }
    }
    public int getCount() {
        return count;
    }
    public static void main(String[] args) throws InterruptedException {
        SynchronizedBlockExample example = new SynchronizedBlockExample();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("Count: " + example.getCount()); // 輸出 2000
    }
}Lock 使用案例
基本使用:
public class LockBasicExample {
    private int count = 0;
    private Lock lock = new ReentrantLock();
    public void increment() {
        lock.lock();
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }
    public int getCount() {
        return count;
    }
    public static void main(String[] args) throws InterruptedException {
        LockBasicExample example = new LockBasicExample();
        Thread thread1 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });
        Thread thread2 = new Thread(() -> {
            for (int i = 0; i < 1000; i++) {
                example.increment();
            }
        });
        thread1.start();
        thread2.start();
        thread1.join();
        thread2.join();
        System.out.println("Count: " + example.getCount()); // 輸出 2000
    }
}可中斷鎖:
public class InterruptibleLockExample {
    private Lock lock = new ReentrantLock();
    public void doTask() {
        try {
            // 可中斷地獲取鎖
            lock.lockInterruptibly();
            try {
                System.out.println(Thread.currentThread().getName() + " 獲取到鎖,開始執(zhí)行任務(wù)");
                Thread.sleep(5000);
            } finally {
                lock.unlock();
                System.out.println(Thread.currentThread().getName() + " 釋放鎖");
            }
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + " 被中斷,未能獲取鎖");
        }
    }
    public static void main(String[] args) throws InterruptedException {
        InterruptibleLockExample example = new InterruptibleLockExample();
        Thread thread1 = new Thread(example::doTask, "Thread-1");
        Thread thread2 = new Thread(example::doTask, "Thread-2");
        thread1.start();
        Thread.sleep(1000); // 確保thread1先獲取到鎖
        thread2.start();
        Thread.sleep(1000);
        thread2.interrupt(); // 中斷thread2
    }
}超時(shí)獲取鎖:
public class TimeoutLockExample {
    private Lock lock = new ReentrantLock();
    public void doTask() {
        try {
            // 超時(shí)獲取鎖,最多等待2秒
            if (lock.tryLock(2, TimeUnit.SECONDS)) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 獲取到鎖,開始執(zhí)行任務(wù)");
                    Thread.sleep(3000);
                } finally {
                    lock.unlock();
                    System.out.println(Thread.currentThread().getName() + " 釋放鎖");
                }
            } else {
                System.out.println(Thread.currentThread().getName() + " 超時(shí)未獲取到鎖");
            }
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + " 被中斷");
        }
    }
    public static void main(String[] args) {
        TimeoutLockExample example = new TimeoutLockExample();
        Thread thread1 = new Thread(example::doTask, "Thread-1");
        Thread thread2 = new Thread(example::doTask, "Thread-2");
        thread1.start();
        thread2.start();
    }
}多條件變量:
public class MultiConditionExample {
    private Lock lock = new ReentrantLock();
    private Condition condition1 = lock.newCondition();
    private Condition condition2 = lock.newCondition();
    private boolean flag1 = false;
    private boolean flag2 = false;
    public void waitForCondition1() throws InterruptedException {
        lock.lock();
        try {
            while (!flag1) {
                condition1.await();
            }
            System.out.println(Thread.currentThread().getName() + " 條件1滿足,執(zhí)行操作");
        } finally {
            lock.unlock();
        }
    }
    public void waitForCondition2() throws InterruptedException {
        lock.lock();
        try {
            while (!flag2) {
                condition2.await();
            }
            System.out.println(Thread.currentThread().getName() + " 條件2滿足,執(zhí)行操作");
        } finally {
            lock.unlock();
        }
    }
    public void signalCondition1() {
        lock.lock();
        try {
            flag1 = true;
            condition1.signal();
            System.out.println("喚醒等待條件1的線程");
        } finally {
            lock.unlock();
        }
    }
    public void signalCondition2() {
        lock.lock();
        try {
            flag2 = true;
            condition2.signal();
            System.out.println("喚醒等待條件2的線程");
        } finally {
            lock.unlock();
        }
    }
    public static void main(String[] args) throws InterruptedException {
        MultiConditionExample example = new MultiConditionExample();
        Thread threadA = new Thread(() -> {
            try {
                example.waitForCondition1();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "Thread-A");
        Thread threadB = new Thread(() -> {
            try {
                example.waitForCondition2();
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }, "Thread-B");
        threadA.start();
        threadB.start();
        Thread.sleep(1000);
        example.signalCondition1();
        Thread.sleep(1000);
        example.signalCondition2();
    }
}














 
 
 












 
 
 
 