【锁1】Synchronized vs ReentrantLock区别

Synchronized vs ReentrantLock 深度对比

一、简介

1. Synchronized(Java 关键字)

Synchronized 是 Java 内置关键字,依托 JVM 实现。它是 Java 中最基本的互斥同步手段,经过版本迭代(Java 6 之后),性能已大幅提升,支持锁升级(偏向锁 -> 轻量级锁 -> 重量级锁)。

核心原理回顾(配合代码理解)

无锁:对象刚创建(开启偏向锁延迟的情况下)。

偏向锁:只有一个线程抢锁,JVM 会把锁偏向这个线程(Mark Word 记录 Thread ID)。

轻量级锁:有多个线程交替执行(轻微竞争),偏向锁撤销,线程通过 CAS + 自旋抢锁。

重量级锁:多线程同时抢锁(激烈竞争),自旋超过阈值,锁膨胀,线程进入 Monitor 等待队列(Blocking)。

java 复制代码
import org.openjdk.jol.info.ClassLayout;

public class SynchronizedUpgradeDemo {

    // 对象头观测对象
    private static final Object LOCK = new Object();

    public static void main(String[] args) throws InterruptedException {
        System.out.println("====== 准备阶段:对象初始状态 ======");
        // 此时可能是无锁或匿名偏向状态
        System.out.println(ClassLayout.parseInstance(LOCK).toPrintable());

        // 休眠一会儿,绕过 JVM 启动时的偏向锁延迟(JDK 8默认4秒)
        Thread.sleep(5000);

        System.out.println("\n====== Case 1: 单线程(偏向锁) ======");
        singleThreadTest();

        // 停顿一下,让上一个锁完全释放
        Thread.sleep(2000);

        System.out.println("\n====== Case 2: 多线程交替(轻量级锁) ======");
        twoThreadsAlternateTest();

        Thread.sleep(2000);

        System.out.println("\n====== Case 3: 多线程争抢(重量级锁) ======");
        multiThreadsContendTest();
    }

    /**
     * 单线程执行同步块
     * 预期:偏向锁(Mark Word 记录当前线程ID)
     */
    private static void singleThreadTest() {
        synchronized (LOCK) {
            System.out.println("单线程持有锁中...");
            System.out.println(ClassLayout.parseInstance(LOCK).toPrintable());
        }
    }

    /**
     * 两个线程交替执行(轻微竞争)
     * 预期:轻量级锁(线程栈中有 Lock Record,CAS 操作)
     */
    private static void twoThreadsAlternateTest() throws InterruptedException {
        Runnable task = () -> {
            synchronized (LOCK) {
                System.out.println(Thread.currentThread().getName() + " 持有锁中...");
                System.out.println(ClassLayout.parseInstance(LOCK).toPrintable());
                try {
                    Thread.sleep(100); // 故意 sleep,让另一个线程处于就绪状态,但不同时抢
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            }
        };

        Thread t1 = new Thread(task, "Thread-A");
        Thread t2 = new Thread(task, "Thread-B");

        t1.start();
        // 确保 t1 先拿到锁
        Thread.sleep(50);
        t2.start();
        t1.join();
        t2.join();
    }

    /**
     * 多个线程疯狂争抢(激烈竞争)
     * 预期:重量级锁(膨胀为 Monitor,线程阻塞/等待)
     */
    private static void multiThreadsContendTest() throws InterruptedException {
        int threadCount = 4;
        Runnable task = () -> {
            // 疯狂抢锁,不 sleep,制造高并发
            for (int i = 0; i < 5; i++) {
                synchronized (LOCK) {
                    // 打印对象头,观察是否变为重量级锁(10)
                    if (i == 0) { // 只打印一次,避免刷屏
                        System.out.println(Thread.currentThread().getName() + " 持有锁中...");
                        System.out.println(ClassLayout.parseInstance(LOCK).toPrintable());
                    }
                }
            }
        };

        Thread[] threads = new Thread[threadCount];
        for (int i = 0; i < threadCount; i++) {
            threads[i] = new Thread(task, "Contend-Thread-" + i);
        }

        for (Thread t : threads) {
            t.start();
        }
        for (Thread t : threads) {
            t.join();
        }
    }
}

2. ReentrantLock(JDK API)

ReentrantLockjava.util.concurrent.locks 包下的一个类,基于 AQS(AbstractQueuedSynchronizer) 实现。它提供了比 synchronized 更灵活、更强大的锁控制能力。

核心架构:AQS 的两大基石

ReentrantLock 内部有一个名为 Sync 的抽象类,它继承了 AbstractQueuedSynchronizer。

AQS 依靠两大核心来维持锁的运作:

volatile int state(同步状态):代表锁被占用的次数。

FIFO 双向队列(CLH 队列变体):存放那些抢锁失败的线程。

  1. 状态位 state 的含义

0:锁未被任何线程占用。

0:锁已被占用。数值代表重入的次数。

例如:线程 A 第一次 lock(),state 从 0 -> 1。

线程 A 再次 lock()(重入),state 从 1 -> 2。

线程 A 执行 unlock(),state 从 2 -> 1。

线程 A 再次 unlock(),state 从 1 -> 0(完全释放)。

  1. CLH 队列(等待队列)

当线程抢锁失败(CAS 设置 state 失败),AQS 会将当前线程包装成一个 Node 节点,通过 CAS + 自旋​ 的方式挂载到队列的尾部,然后通过 LockSupport.park() 挂起线程。

类结构关系
bash 复制代码
ReentrantLock
    ├── Sync (abstract class extends AbstractQueuedSynchronizer)
    │   ├── NonfairSync (默认,非公平锁)
    │   └── FairSync (公平锁)
    └── ConditionObject (内部类,实现多条件队列)
加锁流程(以非公平锁为例)
java 复制代码
// ReentrantLock.java
public void lock() {
    sync.lock(); // 调用内部类的 lock 方法
}

// ReentrantLock.NonfairSync
final void lock() {
    // 1. 【插队】:不管队列里有没有人,先直接 CAS 抢锁
    if (compareAndSetState(0, 1))
        setExclusiveOwnerThread(Thread.currentThread()); // 抢成功,标记我是主人
    else
        acquire(1); // 2. 抢失败,走 AQS 的标准获取流程
}

// AbstractQueuedSynchronizer.java
public final void acquire(int arg) {
    // 3. tryAcquire:再次尝试获取(处理重入逻辑)
    // addWaiter:获取失败,加入队列
    // acquireQueued:在队列中自旋/挂起等待
    if (!tryAcquire(arg) &&
        acquireQueued(addWaiter(Node.EXCLUSIVE), arg))
        selfInterrupt();
}

详细步骤拆解:

场景:线程 A 持有锁,线程 B 来抢锁。

抢占阶段:

线程 B 调用 lock(),首先执行 compareAndSetState(0, 1)

此时 state 是 1(被 A 占用),CAS 失败。

尝试获取(tryAcquire):

进入 acquire(1),调用 tryAcquire(1)。

判断当前线程是不是 exclusiveOwnerThread(持有者)。

如果是(重入),state++,返回成功。

如果不是(这里是线程 B,持有者是 A),返回失败。

入队阶段(addWaiter):

线程 B 被包装成 Node 节点。

通过 CAS 自旋,将节点插入到 CLH 队列的尾部。

挂起阶段(acquireQueued):

线程 B 在队列中检查前驱节点。

如果前驱是 Head 节点,它会再次尝试 tryAcquire(这是自旋等待)。

如果还是失败,调用 LockSupport.park(this),线程 B 阻塞(WAITING 状态),停止空转,等待被唤醒。

解锁流程
java 复制代码
// ReentrantLock.java
public void unlock() {
    sync.release(1);
}

// AbstractQueuedSynchronizer.java
public final boolean release(int arg) {
    if (tryRelease(arg)) { // 1. 尝试释放锁
        Node h = head;
        if (h != null && h.waitStatus != 0)
            unparkSuccessor(h); // 2. 唤醒后继节点
        return true;
    }
    return false;
}

// ReentrantLock.Sync
protected final boolean tryRelease(int releases) {
    int c = getState() - releases; // state 减 1
    if (Thread.currentThread() != getExclusiveOwnerThread())
        throw new IllegalMonitorStateException(); // 不是持有者不能释放
    boolean free = false;
    if (c == 0) { // 只有 state 归零,才真正释放
        free = true;
        setExclusiveOwnerThread(null);
    }
    setState(c); // 写回 state
    return free;
}

详细步骤拆解:

场景:线程 A 执行完业务,调用 unlock()

校验身份:判断当前线程是否是锁的持有者(A),不是则抛异常。

重入计数递减:state = state - 1

判断释放:

如果 state > 0(重入了多次),锁依然被 A 持有,返回 false

如果 state == 0,锁完全释放。将 exclusiveOwnerThread 设为 null

唤醒后继:找到 CLH 队列的头结点,调用 LockSupport.unpark(线程B),唤醒队列中等待最久的线程 B。

线程 B 复活:线程 B 从 park() 处醒来,继续执行 acquireQueued 中的循环,再次尝试 CAS 获取锁。

Condition 的实现原理

ReentrantLock 的 newCondition() 返回的是 AQS 内部的 ConditionObject。

等待队列(单向链表):每个 Condition 对象都维护一个独立的等待队列。

await():线程调用 await() 时,会释放锁(state 归零),并将线程包装成节点放入 Condition 的等待队列,然后 park 挂起。

signal():线程调用 signal() 时,会从 Condition 的等待队列中取出头节点,将其转移到 AQS 的主 CLH 队列中,等待抢锁。

这就是为什么 ReentrantLock 可以实现多条件队列,而 synchronized 只能在一个等待队列上 wait/notify。


二、核心区别对比表

特性维度 Synchronized ReentrantLock 优势方
实现层面 JVM 层面(关键字) JDK 层面(API 类) -
锁的获取 简单,自动获取和释放 手动 lock() / unlock() Synchronized
锁的释放 自动(代码块结束或异常) 手动 (必须在 finally 中释放) Synchronized
公平性 仅支持非公平锁 可选 (构造参数 fair=true ReentrantLock
可中断锁 不支持(等待时无法中断) 支持lockInterruptibly() ReentrantLock
尝试获取 不支持 支持tryLock() ReentrantLock
条件队列 单一等待队列(wait/notify 多条件队列Condition ReentrantLock
性能 Java 6 后优化明显,低竞争下快 高并发下更稳定,减少锁竞争 视场景而定
锁升级 支持(偏向 -> 轻量 -> 重量) 不支持(一直都是重量级逻辑,但实现高效) Synchronized

三、代码示例

java 复制代码
package locktest;

public class SynchronizedDemo {

    private int count = 0;

    private final Object lock = new Object();

    // 1. 修饰代码块
    public void incrementBlock() {
        synchronized (lock) { // 或 synchronized (this)
            System.out.println(Thread.currentThread().getName() + " count: " + count);
            count++;
        }
    }

    // 2. 修饰实例方法
    public synchronized void incrementMethod() {
        count++;
    }

    // 3. 修饰静态方法
    public static synchronized void staticMethod() {
        // 锁住的是 SynchronizedDemo.class
    }

    public static void main(String[] args) {
        SynchronizedDemo demo = new SynchronizedDemo();
        // 启动多个线程测试
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                for (int j = 0; j < 10; j++) {
                    demo.incrementBlock();
                }
            }).start();
        }
    }
}
java 复制代码
package locktest;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class ReentrantLockDemo {

    private int count = 0;

    private final ReentrantLock lock = new ReentrantLock();

    public void increment() {
        lock.lock(); // 获取锁
        try {
            count++;
            System.out.println(Thread.currentThread().getName() + " count: " + count);
        } finally {
            lock.unlock(); // 必须在 finally 中释放锁,否则异常会导致死锁
        }
    }

    /**
     * 用法一:可重入性演示
     * 同一线程可以重复获取同一把锁(state 递增),
     * 只要 lock 与 unlock 次数配对即可,不会像不可重入锁那样自己等自己。
     */
    public void reentrant() {
        lock.lock();
        try {
            System.out.println(Thread.currentThread().getName()
                    + " 第一次获取锁,重入次数: " + lock.getHoldCount());
            lock.lock(); // 重入:同一线程再次获取,不会阻塞
            try {
                System.out.println(Thread.currentThread().getName()
                        + " 第二次获取锁,重入次数: " + lock.getHoldCount());
            } finally {
                lock.unlock();
            }
        } finally {
            lock.unlock();
        }
    }

    /**
     * 用法二:tryLock() 非阻塞尝试获取锁
     * 拿不到锁立刻返回 false,不会挂起线程,适合"拿不到就干别的"的场景,
     * 也是避免死锁的常用手段(拿不到就释放已持有的锁再重试)。
     */
    public void tryLockDemo() {
        if (lock.tryLock()) {
            try {
                System.out.println(Thread.currentThread().getName() + " tryLock 成功,执行业务");
                sleep(1000); // 模拟业务耗时,让其他线程 tryLock 失败
            } finally {
                lock.unlock();
            }
        } else {
            System.out.println(Thread.currentThread().getName() + " tryLock 失败,去做别的事");
        }
    }

    /**
     * 用法三:tryLock(timeout) 限时等待获取锁
     * 在指定时间内没拿到锁就放弃,比无限期 lock() 更可控。
     */
    public void tryLockWithTimeout() {
        try {
        		// 等待500毫秒,拿不到就走
            if (lock.tryLock(500, TimeUnit.MILLISECONDS)) {
                try {
                    System.out.println(Thread.currentThread().getName() + " 500ms 内拿到锁");
                    sleep(1000);
                } finally {
                    lock.unlock();
                }
            } else {
                System.out.println(Thread.currentThread().getName() + " 等待 500ms 超时,放弃获取");
            }
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt(); // 等锁期间被中断
        }
    }

    /**
     * 用法四:lockInterruptibly() 可中断地获取锁
     * 与 lock() 的区别:阻塞等锁期间可以响应 interrupt(),
     * 避免线程在锁上无限期死等(synchronized 做不到这一点)。
     */
    public void lockInterruptiblyDemo() {
        try {
        /**
             * lockInterruptibly() 的作用:以可响应中断的方式获取锁。
             * 它内部大致做了这几件事:
             * 先检查中断标志:如果当前线程在调用时已经被中断,直接抛 InterruptedException,锁都不去抢
             * 尝试获取锁:锁空闲就直接拿到(和 lock() 一样);如果是自己已持有,重入计数 +1
             * 拿不到就挂起等待:进入 AQS 等待队列阻塞,但关键区别在于------阻塞期间如果被其他线程 interrupt(),会立刻从队列中退出并抛出 InterruptedException,而不是继续死等
             */
            lock.lockInterruptibly();
            try {
                System.out.println(Thread.currentThread().getName() + " 获取锁成功");
            } finally {
                lock.unlock();
            }
        } catch (InterruptedException e) {
            System.out.println(Thread.currentThread().getName() + " 等锁时被中断,放弃等待");
        }
    }

    /**
     * 用法五:公平锁
     * new ReentrantLock(true) 创建公平锁,等待最久的线程优先获取,
     * 避免线程饥饿,但吞吐量比默认的非公平锁低。
     */
    static class FairLockDemo {
        private final ReentrantLock fairLock = new ReentrantLock(true);

        public void access() {
            for (int i = 0; i < 2; i++) {
                fairLock.lock();
                try {
                    // 公平模式下各线程大致轮流打印;换成非公平锁则可能被同一线程连续抢占
                    System.out.println(Thread.currentThread().getName() + " 获取到公平锁");
                } finally {
                    fairLock.unlock();
                }
            }
        }
    }

    /**
     * 用法六:Condition 实现等待/通知(生产者-消费者)
     * 一把锁可以创建多个 Condition,实现精确唤醒;
     * 相比 synchronized 的 wait/notify 只有一个等待队列更灵活。
     */
    static class BoundedQueue {
        private final Queue<Integer> queue = new LinkedList<>();
        private final int capacity = 3;

        private final ReentrantLock lock = new ReentrantLock();
        private final Condition notFull = lock.newCondition();  // 队列未满,生产者等在这里
        private final Condition notEmpty = lock.newCondition(); // 队列非空,消费者等在这里

        public void put(int value) throws InterruptedException {
            lock.lock();
            try {
                while (queue.size() == capacity) { // 必须用 while 防止虚假唤醒
                    notFull.await(); // 释放锁并等待
                }
                queue.offer(value);
                System.out.println(Thread.currentThread().getName() + " 生产: " + value);
                notEmpty.signal(); // 只唤醒消费者,不会误唤醒其他生产者
            } finally {
                lock.unlock();
            }
        }

        public int take() throws InterruptedException {
            lock.lock();
            try {
                while (queue.isEmpty()) {
                    notEmpty.await();
                }
                int value = queue.poll();
                System.out.println(Thread.currentThread().getName() + " 消费: " + value);
                notFull.signal(); // 只唤醒生产者
                return value;
            } finally {
                lock.unlock();
            }
        }
    }

    public static void main(String[] args) throws InterruptedException {
        ReentrantLockDemo demo = new ReentrantLockDemo();
        // 启动多个线程测试
        for (int i = 0; i < 5; i++) {
            new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    demo.increment();
                }
            }).start();
        }
        sleep(1000);

        System.out.println("\n===== 1. 可重入性 =====");
        demo.reentrant();

        System.out.println("\n===== 2. tryLock 非阻塞获取 =====");
        Thread t1 = new Thread(demo::tryLockDemo, "tryLock-线程1");
        Thread t2 = new Thread(demo::tryLockDemo, "tryLock-线程2"); // 大概率失败
        t1.start();
        sleep(100);
        t2.start();
        t1.join();
        t2.join();

        System.out.println("\n===== 3. tryLock 超时获取 =====");
        Thread t3 = new Thread(demo::tryLockDemo, "占锁线程"); // 先占锁 1 秒
        Thread t4 = new Thread(demo::tryLockWithTimeout, "限时等待线程"); // 等 500ms 超时
        t3.start();
        sleep(100);
        t4.start();
        t3.join();
        t4.join();

        System.out.println("\n===== 4. lockInterruptibly 可中断等锁 =====");
        demo.lock.lock(); // 主线程先占住锁
        Thread t5 = new Thread(demo::lockInterruptiblyDemo, "可中断线程");
        t5.start();
        sleep(100);
        t5.interrupt(); // 中断等锁中的线程,它会立刻退出而不是死等
        t5.join();
        demo.lock.unlock();

        System.out.println("\n===== 5. 公平锁 =====");
        FairLockDemo fair = new FairLockDemo();
        for (int i = 0; i < 3; i++) {
            new Thread(fair::access, "公平锁-线程" + i).start();
        }
        sleep(500);

        System.out.println("\n===== 6. Condition 生产者-消费者 =====");
        BoundedQueue boundedQueue = new BoundedQueue();
        new Thread(() -> {
            try {
                for (int i = 1; i <= 5; i++) {
                    boundedQueue.put(i);
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "生产者").start();
        new Thread(() -> {
            try {
                for (int i = 1; i <= 5; i++) {
                    boundedQueue.take();
                    sleep(200); // 消费慢于生产,队列会被填满,触发 notFull.await()
                }
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }, "消费者").start();
    }

    private static void sleep(long millis) {
        try {
            TimeUnit.MILLISECONDS.sleep(millis);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        }
    }
}
bash 复制代码
Thread-4 count: 47
Thread-4 count: 48
Thread-4 count: 49
Thread-4 count: 50

===== 1. 可重入性 =====
main 第一次获取锁,重入次数: 1
main 第二次获取锁,重入次数: 2

===== 2. tryLock 非阻塞获取 =====
tryLock-线程1 tryLock 成功,执行业务
tryLock-线程2 tryLock 失败,去做别的事

===== 3. tryLock 超时获取 =====
占锁线程 tryLock 成功,执行业务
限时等待线程 等待 500ms 超时,放弃获取

===== 4. lockInterruptibly 可中断等锁 =====
可中断线程 等锁时被中断,放弃等待

===== 5. 公平锁 =====
公平锁-线程0 获取到公平锁
公平锁-线程1 获取到公平锁
公平锁-线程2 获取到公平锁
公平锁-线程0 获取到公平锁
公平锁-线程1 获取到公平锁
公平锁-线程2 获取到公平锁

===== 6. Condition 生产者-消费者 =====
生产者 生产: 1
生产者 生产: 2
生产者 生产: 3
消费者 消费: 1
生产者 生产: 4
消费者 消费: 2
生产者 生产: 5
消费者 消费: 3
消费者 消费: 4
消费者 消费: 5

进程已结束,退出代码为 0

异常场景对比

java 复制代码
import java.util.concurrent.locks.ReentrantLock;

public class ExceptionDemo {
    // Synchronized:异常自动释放锁
    public synchronized void synchronizedMethod() {
        System.out.println("Synchronized 获取锁");
        throw new RuntimeException("Synchronized 异常"); // 锁会自动释放
    }

    // ReentrantLock:异常不会自动释放锁,必须手动处理
    private final ReentrantLock lock = new ReentrantLock();

    public void reentrantLockMethod() {
        lock.lock();
        try {
            System.out.println("ReentrantLock 获取锁");
            throw new RuntimeException("ReentrantLock 异常");
        } finally {
            lock.unlock(); // 必须手动释放,否则锁永远不释放
            System.out.println("ReentrantLock 锁已释放");
        }
    }

    public static void main(String[] args) {
        ExceptionDemo demo = new ExceptionDemo();

        // 测试 Synchronized
        try {
            demo.synchronizedMethod();
        } catch (Exception e) {
            System.out.println("捕获异常: " + e.getMessage());
        }

        System.out.println("---");

        // 测试 ReentrantLock
        try {
            demo.reentrantLockMethod();
        } catch (Exception e) {
            System.out.println("捕获异常: " + e.getMessage());
        }
    }
}

四、如何选择?

使用 Synchronized 的情况:

  1. 简单场景:简单的同步块或方法。
  2. 代码简洁性 :不想处理 try/finally 和手动解锁。
  3. 低竞争环境:JVM 会自动进行锁优化(偏向锁、轻量级锁),性能很好。
  4. 资源受限:不需要高级功能(如中断、公平锁、多条件)。

使用 ReentrantLock 的情况:

  1. 高并发竞争:需要更精细的并发控制。
  2. 需要可中断锁:避免死等。
  3. 需要尝试获取锁:防止死锁,实现非阻塞算法。
  4. 需要公平锁:防止线程饥饿。
  5. 复杂同步结构 :需要使用多个 Condition 进行精准线程协作。

五、总结

能用 synchronized 就用 synchronized,除非你需要 ReentrantLock 的高级特性。

synchronized 是 JVM 的内置公民,随着 JDK 版本的升级不断优化,且写法简单不易出错。ReentrantLock 则是并发专家的工具箱,当你需要打破常规(如中断、限时等待、公平性等)时,它是唯一的选择。

面试速记口诀:

  • Synchronized:简单自动,非公平,单一条件
  • ReentrantLock:手动灵活,可公平,多条件,可中断,可尝试

六、最佳实践

  1. 锁的范围最小化:只锁必要的代码块
  2. 避免嵌套锁:减少死锁风险
  3. 使用超时机制tryLock 防止死锁
  4. 正确处理异常ReentrantLock 必须在 finally 中释放
  5. 考虑性能 :低竞争用 synchronized,高并发用 ReentrantLock

七、常见面试题

  1. Q: Synchronized 和 ReentrantLock 哪个性能更好?

    A: Java 6 之后,低竞争下 synchronized 性能更好(JVM 优化);高并发下 ReentrantLock 更稳定。

  2. Q: 什么情况下会发生死锁?如何避免?

    A: 多个线程互相等待对方释放锁时发生死锁。避免方法:按顺序获取锁、使用超时、避免嵌套锁。

  3. Q: ReentrantLock 的 Condition 有什么优势?

    A: 可以创建多个等待队列,实现精准唤醒,避免 notifyAll() 唤醒所有线程造成的性能浪费。

  4. Q: 公平锁一定比非公平锁好吗?

    A: 不一定。公平锁保证顺序但性能较低(线程切换开销);非公平锁吞吐量更高,但可能导致线程饥饿。

  5. Synchronized 为什么要有锁升级?

    锁升级是为了平衡内存开销和 CPU 开销。

    偏向锁:在无竞争时,连 CAS 操作都省略了,仅仅在对象头存个 ID,性能最好(空间换时间)。

    轻量级锁:在竞争很少时,使用 CAS 自旋,避免线程挂起和唤醒的系统调用开销(CPU 换时间)。

    重量级锁:在竞争激烈时,让拿不到锁的线程进入阻塞状态(Park),释放 CPU 资源,避免空转(时间换资源)。


相关推荐
立心者02 小时前
SpringBoot中使用TOTP实现MFA(多因素认证)
java·spring boot·后端
枕星而眠2 小时前
C++ STL Map容器完全指南:从有序红黑树到无序哈希表
java·开发语言
bbq粉刷匠3 小时前
HashMap 底层原理深度拆解(二):putVal 完整链路解析(懒加载 · 链表遍历 · 尾插法)
java·开发语言·哈希算法
Sam_Deep_Thinking3 小时前
一个靠谱的C端网关服务应该包含什么内容
java·程序员·系统架构
杨运交3 小时前
[055][调度模块]Spring动态任务调度框架的设计与实现
java·后端·spring
白狐_7984 小时前
408 数据结构算法题 01:线性表暴力求解保分指南
java·数据结构·算法
流云鹤4 小时前
03Java学习day(3)
java·学习
tqs_123454 小时前
Agent智能体+Skill插件引擎+Milvus混合检索 技术沉淀
java·python·milvus
严同学正在努力5 小时前
从备份到恢复:我用 30 分钟恢复了误删的核心业务表
android·java·数据库·ai