【锁2】锁的分类与概念

Java 锁机制全面详解

本文系统梳理 Java 中各类锁的核心概念、适用场景及代码实现,助你彻底搞懂并发编程中的"锁"事。


目录

  • [一、悲观锁 vs 乐观锁](#一、悲观锁 vs 乐观锁)
  • [二、公平锁 vs 非公平锁](#二、公平锁 vs 非公平锁)
  • [三、可重入锁 vs 不可重入锁](#三、可重入锁 vs 不可重入锁)
  • [四、自旋锁 vs 阻塞锁](#四、自旋锁 vs 阻塞锁)
  • [五、偏向锁 / 轻量级锁 / 重量级锁](#五、偏向锁 / 轻量级锁 / 重量级锁)
  • 六、各类锁速查对照表

一、悲观锁 vs 乐观锁

1.1 核心思想对比

维度 悲观锁 乐观锁
核心假设 并发冲突很严重,每次操作都会冲突 并发冲突不严重,多数操作不会冲突
加锁方式 操作数据前先加锁 不加锁,通过版本号/CAS 检测冲突
典型实现 synchronizedReentrantLock、数据库 SELECT FOR UPDATE 数据库 version 字段、AtomicInteger、CAS 操作
适用场景 写多读少、冲突频繁 读多写少、冲突较少
性能特点 加锁开销大,但冲突时安全 无锁开销小,但冲突频繁时重试成本高

1.2 悲观锁代码示例

(1)synchronized 实现悲观锁
java 复制代码
/**
 * 使用 synchronized 实现悲观锁
 * 每次只能有一个线程进入同步方法,其他线程阻塞等待
 */
public class PessimisticLockDemo {

    private int count = 0;

    // synchronized 修饰实例方法 → 锁住当前对象
    public synchronized void increment() {
        count++;
    }

    // synchronized 修饰代码块 → 锁住指定对象
    public void incrementBlock() {
        synchronized (this) {
            count++;
        }
    }

    // 锁住 class 对象 → 类级别的锁
    public static synchronized void staticMethod() {
        System.out.println("类级悲观锁");
    }

    public int getCount() {
        return count;
    }

    // ===== 测试 =====
    public static void main(String[] args) throws InterruptedException {
        PessimisticLockDemo demo = new PessimisticLockDemo();
        int threadCount = 10;
        Thread[] threads = new Thread[threadCount];

        for (int i = 0; i < threadCount; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    demo.increment();
                }
            });
            threads[i].start();
        }

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

        System.out.println("最终结果: " + demo.getCount()); // 10000
    }
}
(2)ReentrantLock 实现悲观锁
java 复制代码
import java.util.concurrent.locks.ReentrantLock;

/**
 * 使用 ReentrantLock 实现悲观锁
 * 比 synchronized 更灵活:可中断、可超时、可公平
 */
public class ReentrantLockPessimisticDemo {

    private final ReentrantLock lock = new ReentrantLock();
    private int count = 0;

    public void increment() {
        lock.lock();  // 获取锁(悲观:假设一定会冲突)
        try {
            count++;
        } finally {
            lock.unlock();  // 必须在 finally 中释放
        }
    }

    // 可中断的加锁方式
    public void incrementInterruptibly() throws InterruptedException {
        lock.lockInterruptibly();  // 允许被中断
        try {
            count++;
        } finally {
            lock.unlock();
        }
    }

    // 带超时的加锁方式
    public boolean incrementWithTimeout() throws InterruptedException {
        // 最多等 3 秒
        if (lock.tryLock(3, java.util.concurrent.TimeUnit.SECONDS)) {
            try {
                count++;
                return true;
            } finally {
                lock.unlock();
            }
        }
        return false;  // 超时未获取到锁
    }

    public int getCount() {
        return count;
    }
}
(3)数据库层面的悲观锁
sql 复制代码
-- MySQL:查询时加排他锁(悲观锁)
-- 事务未提交前,其他事务无法修改这条数据
START TRANSACTION;
SELECT * FROM account WHERE id = 1 FOR UPDATE;
UPDATE account SET balance = balance - 100 WHERE id = 1;
COMMIT;

1.3 乐观锁代码示例

(1)版本号机制(数据库乐观锁)
sql 复制代码
-- 建表时增加 version 字段
CREATE TABLE account (
    id      BIGINT PRIMARY KEY,
    balance DECIMAL(10, 2),
    version INT DEFAULT 0
);

-- 更新时校验版本号(乐观:假设不会冲突)
UPDATE account 
   SET balance = balance - 100, 
       version = version + 1
 WHERE id = 1 
   AND version = 0;  -- 只有版本号匹配才更新成功

-- 如果返回 affected rows = 0,说明数据已被其他事务修改,需要重试
java 复制代码
/**
 * 数据库乐观锁 Java 实现
 */
public class OptimisticLockDBDemo {

    @Autowired
    private JdbcTemplate jdbcTemplate;

    public boolean transfer(Long accountId, BigDecimal amount) {
        int maxRetry = 3;
        for (int i = 0; i < maxRetry; i++) {
            // 1. 查询当前数据和版本号
            Map<String, Object> row = jdbcTemplate.queryForMap(
                "SELECT balance, version FROM account WHERE id = ?", accountId);
            BigDecimal balance = (BigDecimal) row.get("balance");
            int version = (int) row.get("version");

            // 2. 计算新余额
            BigDecimal newBalance = balance.subtract(amount);
            if (newBalance.compareTo(BigDecimal.ZERO) < 0) {
                return false; // 余额不足
            }

            // 3. 带版本号更新(CAS 思想)
            int updated = jdbcTemplate.update(
                "UPDATE account SET balance = ?, version = version + 1 " +
                "WHERE id = ? AND version = ?",
                newBalance, accountId, version);

            if (updated > 0) {
                return true;  // 更新成功
            }
            // updated == 0 说明版本号已被其他线程修改,重试
            System.out.println("版本冲突,重试第 " + (i + 1) + " 次");
        }
        return false; // 重试多次仍失败
    }
}
(2)CAS 机制(AtomicInteger 乐观锁)
java 复制代码
import java.util.concurrent.atomic.AtomicInteger;

/**
 * 使用 CAS(Compare-And-Swap)实现乐观锁
 * 底层调用 Unsafe.compareAndSwapInt,无锁并发
 */
public class CASOptimisticDemo {

    private final AtomicInteger count = new AtomicInteger(0);

    // CAS 自旋重试
    public void increment() {
        while (true) {
            int oldValue = count.get();
            int newValue = oldValue + 1;
            // 乐观:期望值是 oldValue,如果是则更新为 newValue
            if (count.compareAndSet(oldValue, newValue)) {
                break; // CAS 成功,退出
            }
            // CAS 失败 → 说明有别的线程改了,重试
        }
    }

    // 更简洁的方式(内部已做 CAS)
    public void incrementSimple() {
        count.incrementAndGet();
    }

    public int getCount() {
        return count.get();
    }

    // ===== 测试 =====
    public static void main(String[] args) throws InterruptedException {
        CASOptimisticDemo demo = new CASOptimisticDemo();
        int threadCount = 10;
        Thread[] threads = new Thread[threadCount];

        for (int i = 0; i < threadCount; i++) {
            threads[i] = new Thread(() -> {
                for (int j = 0; j < 1000; j++) {
                    demo.increment();
                }
            });
            threads[i].start();
        }

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

        System.out.println("最终结果: " + demo.getCount()); // 10000
    }
}
(3)AtomicReference 实现自定义对象的乐观锁 (可解决ABA问题)
java 复制代码
import java.util.concurrent.atomic.AtomicReference;

class Account {
    private final String userId;
    private final BigDecimal balance;

    public Account(String userId, BigDecimal balance) {
        this.userId = userId;
        this.balance = balance;
    }
    // getters...
}

/**
 * 使用 AtomicReference + CAS 实现对象级别的乐观锁
 */
public class AtomicReferenceDemo {

    private final AtomicReference<Account> accountRef =
        new AtomicReference<>(new Account("user1", new BigDecimal("1000")));

    public boolean withdraw(BigDecimal amount) {
        while (true) {
            Account current = accountRef.get();
            BigDecimal newBalance = current.getBalance().subtract(amount);

            if (newBalance.compareTo(BigDecimal.ZERO) < 0) {
                return false; // 余额不足
            }

            Account updated = new Account(current.getUserId(), newBalance);
            // CAS:只有当前引用没被其他线程修改才更新
            if (accountRef.compareAndSet(current, updated)) {
                return true;
            }
            // 失败则重试
        }
    }
}

二、公平锁 vs 非公平锁

2.1 核心思想对比

维度 公平锁 非公平锁
获取顺序 严格按照 FIFO 队列顺序,先到先得 允许新来的线程"插队"先抢锁
吞吐量 较低(线程切换多) 较高(减少上下文切换)
饥饿问题 无饥饿 可能导致队列尾部线程饥饿
典型实现 new ReentrantLock(true) synchronizednew ReentrantLock(false)

2.2 公平锁代码示例

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

/**
 * 公平锁:严格按照线程请求顺序获取锁
 * 先来先得,绝不插队
 */
public class FairLockDemo {

    // true → 公平锁
    private final ReentrantLock fairLock = new ReentrantLock(true);

    public void doWork(String taskName) {
        fairLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() 
                + " 获取到公平锁,执行任务: " + taskName);
            Thread.sleep(500); // 模拟业务耗时
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            fairLock.unlock();
        }
    }

    public static void main(String[] args) {
        Test demo = new Test();
        // 按顺序启动 5 个线程
        for (int i = 1; i <= 3; i++) {
            final int idx = i;
            new Thread(() -> {
                for (int j = 0; j < 5; j++) {
                    demo.doWork("Task-" + idx + "-" + j);
                }
            }, "Thread-" + idx).start();
        }
        // 输出顺序:每轮循环线程顺序相等
    }
}
bash 复制代码
Thread-3 获取到公平锁,执行任务: Task-3-0
Thread-2 获取到公平锁,执行任务: Task-2-0
Thread-1 获取到公平锁,执行任务: Task-1-0
Thread-3 获取到公平锁,执行任务: Task-3-1
Thread-2 获取到公平锁,执行任务: Task-2-1
Thread-1 获取到公平锁,执行任务: Task-1-1
Thread-3 获取到公平锁,执行任务: Task-3-2
Thread-2 获取到公平锁,执行任务: Task-2-2
Thread-1 获取到公平锁,执行任务: Task-1-2
Thread-3 获取到公平锁,执行任务: Task-3-3
Thread-2 获取到公平锁,执行任务: Task-2-3
Thread-1 获取到公平锁,执行任务: Task-1-3
Thread-3 获取到公平锁,执行任务: Task-3-4
Thread-2 获取到公平锁,执行任务: Task-2-4
Thread-1 获取到公平锁,执行任务: Task-1-4

2.3 非公平锁代码示例

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

/**
 * 非公平锁:新来的线程可以先尝试抢锁
 * 抢不到再乖乖排队
 */
public class NonFairLockDemo {

    // false → 非公平锁(默认)
    private final ReentrantLock nonFairLock = new ReentrantLock(false);

    public void doWork(String taskName) {
        nonFairLock.lock();
        try {
            System.out.println(Thread.currentThread().getName() 
                + " 获取到非公平锁,执行: " + taskName);
            Thread.sleep(500);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            nonFairLock.unlock();
        }
    }

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

        // 非公平场景下,后到的线程可能先拿到锁
        for (int i = 1; i <= 5; i++) {
            final int idx = i;
            new Thread(() -> demo.doWork("Task-" + idx), "Thread-" + idx).start();
        }
        // 输出顺序不确定,可能出现"插队"现象
    }
}

2.4 synchronized 的非公平性演示

java 复制代码
/**
 * synchronized 是非公平锁
 * 无法保证线程获取锁的顺序
 */
public class SynchronizedNonFairDemo {

    public synchronized void method() {
        System.out.println(Thread.currentThread().getName() + " 获取锁");
        try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
    }

    public static void main(String[] args) {
        SynchronizedNonFairDemo demo = new SynchronizedNonFairDemo();
        for (int i = 0; i < 5; i++) {
            new Thread(demo::method, "T" + i).start();
        }
        // 执行顺序 ≠ 启动顺序(非公平)
    }
}

三、可重入锁 vs 不可重入锁

3.1 核心思想对比

维度 可重入锁 不可重入锁
同一线程重复获取 ✅ 可以,计数 +1 ❌ 不可以,会死锁
典型实现 synchronizedReentrantLock 自定义简易锁
实现原理 记录持有线程 + 计数 仅记录是否被占用

3.2 可重入锁代码示例

(1)synchronized 可重入
java 复制代码
/**
 * synchronized 天然支持可重入
 * 同一线程可以多次进入同步方法而不会被自己锁住
 */
public class SynchronizedReentrantDemo {

    public synchronized void methodA() {
        System.out.println("methodA 执行中...");
        methodB(); // 同一线程再次获取锁 → 可重入
    }

    public synchronized void methodB() {
        System.out.println("methodB 执行中(可重入)...");
    }

    public static void main(String[] args) {
        new SynchronizedReentrantDemo().methodA();
        // 输出:
        // methodA 执行中...
        // methodB 执行中(可重入)...
    }
}
(2)ReentrantLock 可重入
java 复制代码
import java.util.concurrent.locks.ReentrantLock;

/**
 * ReentrantLock 可重入演示
 * 同一线程多次 lock(),需要对应次数 unlock()
 */
public class ReentrantLockDemo {

    private final ReentrantLock lock = new ReentrantLock();

    public void outerMethod() {
        lock.lock();
        try {
            System.out.println("外层方法获取锁,锁计数: " + lock.getHoldCount());
            innerMethod(); // 递归调用,再次获取同一把锁
        } finally {
            lock.unlock();
        }
    }

    public void innerMethod() {
        lock.lock();
        try {
            System.out.println("内层方法获取锁,锁计数: " + lock.getHoldCount());
        } finally {
            lock.unlock();
        }
    }

    public static void main(String[] args) {
        new ReentrantLockDemo().outerMethod();
        // 输出:
        // 外层方法获取锁,锁计数: 1
        // 内层方法获取锁,锁计数: 2
    }
}

3.3 不可重入锁代码示例

java 复制代码
/**
 * 自定义不可重入锁
 * 同一线程第二次获取锁时会阻塞(死锁)
 */
public class NonReentrantLock {

    private boolean isLocked = false;
    private Thread lockedBy = null;

    public synchronized void lock() throws InterruptedException {
        // 如果锁已被占用,且不是当前线程持有 → 等待
        while (isLocked && lockedBy != Thread.currentThread()) {
            wait();
        }
        isLocked = true;
        lockedBy = Thread.currentThread();
    }

    public synchronized void unlock() {
        if (lockedBy == Thread.currentThread()) {
            isLocked = false;
            lockedBy = null;
            notify();
        }
    }

    // ===== 测试不可重入的后果 =====
    public static void main(String[] args) {
        NonReentrantLock lock = new NonReentrantLock();

        new Thread(() -> {
            try {
                lock.lock();
                System.out.println("第一次获取锁成功");

                // 同一线程再次获取 → 不可重入,此处会死锁!
                lock.lock(); // ⚠️ 这里会永远阻塞
                System.out.println("第二次获取锁成功(不可重入锁永远到不了这里)");

                lock.unlock();
                lock.unlock();
            } catch (InterruptedException e) {
                Thread.currentThread().interrupt();
            }
        }).start();
    }
}

四、自旋锁 vs 阻塞锁

4.1 核心思想对比

维度 自旋锁 阻塞锁
获取锁失败时的行为 循环重试(忙等) 线程挂起,等待唤醒
CPU 消耗 高(占用 CPU 空转) 低(让出 CPU)
响应延迟 极低(不用上下文切换) 较高(需内核态切换)
适用场景 锁持有时间极短(纳秒~微秒级) 锁持有时间较长
典型实现 CAS 自旋、AtomicInteger synchronizedReentrantLock

4.2 自旋锁代码示例

java 复制代码
import java.util.concurrent.atomic.AtomicReference;

/**
 * 自定义自旋锁
 * 获取锁失败时不断循环尝试,不放弃 CPU
 */
public class SpinLock {

    private final AtomicReference<Thread> owner = new AtomicReference<>();

    /**
     * 自旋获取锁
     * 不断 CAS 尝试,直到成功
     */
    public void lock() {
        Thread current = Thread.currentThread();
        // 自旋:不断尝试将 owner 从 null 改为当前线程
        while (!owner.compareAndSet(null, current)) {
            // 可选:让出 CPU,减少空转消耗
            Thread.yield();
        }
    }

    /**
     * 释放锁
     */
    public void unlock() {
        Thread current = Thread.currentThread();
        owner.compareAndSet(current, null);
    }

    // ===== 测试 =====
    public static void main(String[] args) throws InterruptedException {
        SpinLock spinLock = new SpinLock();
        int[] counter = {0};

        Runnable task = () -> {
            for (int i = 0; i < 10000; i++) {
                spinLock.lock();
                try {
                    counter[0]++;
                } finally {
                    spinLock.unlock();
                }
            }
        };

        Thread t1 = new Thread(task);
        Thread t2 = new Thread(task);
        t1.start();
        t2.start();
        t1.join();
        t2.join();

        System.out.println("最终结果: " + counter[0]); // 20000
    }
}

4.3 自适应自旋锁(优化版)

java 复制代码
import java.util.concurrent.atomic.AtomicReference;

/**
 * 带最大自旋次数的自适应自旋锁
 * 防止长时间空转浪费 CPU
 */
public class AdaptiveSpinLock {

    private final AtomicReference<Thread> owner = new AtomicReference<>();
    private static final int MAX_SPIN = 1000; // 最大自旋次数

    public boolean tryLock() {
        Thread current = Thread.currentThread();
        return owner.compareAndSet(null, current);
    }

    public void lock() {
        Thread current = Thread.currentThread();
        int spinCount = 0;

        while (!owner.compareAndSet(null, current)) {
            spinCount++;
            if (spinCount >= MAX_SPIN) {
                // 自旋次数过多,退化为阻塞
                try {
                    Thread.sleep(1); // 让出 CPU
                } catch (InterruptedException e) {
                    Thread.currentThread().interrupt();
                    return;
                }
                spinCount = 0; // 重置计数
            } else {
                Thread.yield(); // 轻量级让出
            }
        }
    }

    public void unlock() {
        Thread current = Thread.currentThread();
        owner.compareAndSet(current, null);
    }
}

4.4 阻塞锁代码示例

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

/**
 * 阻塞锁:获取不到锁时线程挂起
 * 由 JVM/OS 管理线程状态切换
 */
public class BlockingLockDemo {

    private final ReentrantLock lock = new ReentrantLock();
    private int sharedData = 0;

    public void longTask() {
        lock.lock(); // 获取不到锁 → 线程挂起(BLOCKED / WAITING 状态)
        try {
            System.out.println(Thread.currentThread().getName() + " 获取锁");
            sharedData++;
            Thread.sleep(2000); // 模拟耗时操作(锁持有时间长)
            System.out.println(Thread.currentThread().getName() + " 完成,数据=" + sharedData);
        } catch (InterruptedException e) {
            Thread.currentThread().interrupt();
        } finally {
            lock.unlock();
        }
    }

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

        for (int i = 0; i < 3; i++) {
            new Thread(demo::longTask, "Worker-" + i).start();
        }
        // 线程会依次执行,未获取到锁的线程进入 BLOCKED 状态
        // 通过 jstack 可以看到线程状态为 BLOCKED
    }
}

4.5 自旋锁 vs 阻塞锁 选择建议

java 复制代码
/**
 * 实战建议:根据锁持有时间选择策略
 */
public class LockStrategyGuide {

    // ✅ 适合自旋锁:操作极快(几行代码)
    public void shortOperation() {
        // 例如:计数器递增、状态标志翻转
        // 耗时:纳秒~微秒级 → 自旋代价 < 上下文切换代价
    }

    // ✅ 适合阻塞锁:操作耗时较长
    public void longOperation() {
        // 例如:IO 操作、网络调用、复杂计算
        // 耗时:毫秒级以上 → 上下文切换代价 < 自旋代价
    }
}

五、偏向锁 / 轻量级锁 / 重量级锁

这三种锁是 synchronized 在 JVM 层面的锁升级机制,目的是在无竞争→低竞争→高竞争的不同场景下,自动选择最优的加锁策略。

5.1 锁升级流程

复制代码
无锁 ──(第一个线程进入)──→ 偏向锁 ──(第二个线程竞争)──→ 轻量级锁 ──(竞争激烈)──→ 重量级锁

5.2 三种锁对比

锁状态 触发条件 实现机制 开销
偏向锁 只有一个线程访问同步块 在对象头 Mark Word 记录线程 ID 几乎为零
轻量级锁 多个线程交替执行,无真正竞争 CAS 自旋 + 栈帧锁记录 较小
重量级锁 多线程同时竞争 操作系统互斥量(mutex)+ 线程阻塞/唤醒 较大

5.3 代码示例与验证

java 复制代码
/**
 * synchronized 锁升级演示
 * 
 * 注意:需要关闭偏向锁延迟才能观察偏向锁
 * JVM 参数:-XX:BiasedLockingStartupDelay=0
 *           -XX:+UseBiasedLocking
 *           -XX:+PrintFlagsFinal
 * 
 * 打印锁状态工具:-XX:+PrintBiasedLockingStatistics
 */
public class LockUpgradeDemo {

    private static final Object lock = new Object();

    public static void main(String[] args) throws InterruptedException {
        // ===== 阶段1:偏向锁 =====
        // 只有一个线程访问 → JVM 使用偏向锁
        synchronized (lock) {
            System.out.println("阶段1:单线程获取锁(偏向锁)");
            printLockState(lock);
        }

        // ===== 阶段2:轻量级锁 =====
        // 多个线程交替执行,无真正竞争
        Thread t1 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("阶段2:线程1交替执行(轻量级锁)");
                printLockState(lock);
            }
        });
        t1.start();
        t1.join();

        Thread t2 = new Thread(() -> {
            synchronized (lock) {
                System.out.println("阶段2:线程2交替执行(轻量级锁)");
                printLockState(lock);
            }
        });
        t2.start();
        t2.join();

        // ===== 阶段3:重量级锁 =====
        // 多线程同时竞争 → 升级为重量级锁
        int threadCount = 4;
        Thread[] threads = new Thread[threadCount];
        for (int i = 0; i < threadCount; i++) {
            threads[i] = new Thread(() -> {
                synchronized (lock) {
                    System.out.println(Thread.currentThread().getName() 
                        + " 竞争锁(重量级锁)");
                    try { Thread.sleep(1000); } catch (InterruptedException ignored) {}
                }
            }, "CompeteThread-" + i);
        }

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

    /**
     * 打印对象锁状态(通过反射读取 Mark Word)
     * 实际项目中可使用 JOL (Java Object Layout) 工具
     */
    private static void printLockState(Object obj) {
        // 使用 org.openjdk.jol:jol-core 工具打印
        // System.out.println(ClassLayout.parseInstance(obj).toPrintable());
        // 输出示例:
        // 偏向锁:  mark word = 0x... | 101  (末尾 101 = biased)
        // 轻量级:  mark word = 0x... | 00   (末尾 00 = lightweight)
        // 重量级:  mark word = 0x... | 10   (末尾 10 = heavyweight)
        System.out.println("  [查看 Mark Word 需引入 JOL 依赖]");
    }
}

5.4 使用 JOL 工具观察锁状态

xml 复制代码
<!-- pom.xml 添加依赖 -->
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.17</version>
</dependency>
java 复制代码
import org.openjdk.jol.info.ClassLayout;

/**
 * 使用 JOL 精确查看对象头中的锁状态
 * 运行参数:-XX:BiasedLockingStartupDelay=0
 */
public class JOLDemo {

    public static void main(String[] args) throws InterruptedException {
        Object obj = new Object();

        // 1. 无锁状态
        System.out.println("===== 无锁 =====");
        System.out.println(ClassLayout.parseInstance(obj).toPrintable());

        // 2. 偏向锁
        synchronized (obj) {
            System.out.println("===== 偏向锁 =====");
            System.out.println(ClassLayout.parseInstance(obj).toPrintable());
        }

        // 3. 轻量级锁(另一个线程交替访问)
        Thread t = new Thread(() -> {
            synchronized (obj) {
                System.out.println("===== 轻量级锁 =====");
                System.out.println(ClassLayout.parseInstance(obj).toPrintable());
            }
        });
        t.start();
        t.join();

        // 4. 重量级锁(多线程竞争)
        for (int i = 0; i < 2; i++) {
            new Thread(() -> {
                synchronized (obj) {
                    System.out.println("===== 重量级锁 =====");
                    System.out.println(ClassLayout.parseInstance(obj).toPrintable());
                    try { Thread.sleep(2000); } catch (InterruptedException ignored) {}
                }
            }).start();
        }

        Thread.sleep(3000);
    }
}

输出解读(Mark Word 末尾 2~3 bit):

锁状态 Mark Word 标志 含义
无锁 001 未加锁
偏向锁 101 偏向某个线程
轻量级锁 00 栈帧中 CAS 自旋
重量级锁 10 操作系统 mutex
GC 标记 11 垃圾回收中

六、各类锁速查对照表

6.1 全景对照表

分类维度 类型 核心特征 典型实现 适用场景
并发态度 悲观锁 先加锁再操作 synchronizedReentrantLockSELECT FOR UPDATE 写多读少
乐观锁 不加锁,CAS/版本号检测 AtomicInteger、数据库 version 读多写少
公平性 公平锁 FIFO 严格排队 new ReentrantLock(true) 需避免饥饿
非公平锁 允许插队 synchronized、默认 ReentrantLock 追求吞吐量
可重入性 可重入锁 同一线程可重复获取 synchronizedReentrantLock 递归/嵌套调用
不可重入锁 重复获取会死锁 自定义简易锁 特殊场景
等待策略 自旋锁 忙等重试 CAS 实现 锁持有时间极短
阻塞锁 挂起等待唤醒 synchronized、AQS 锁持有时间较长
JVM 锁升级 偏向锁 单线程,记录 Thread ID synchronized 底层 无竞争
轻量级锁 多线程交替,CAS 自旋 synchronized 底层 低竞争
重量级锁 多线程同时竞争 synchronized 底层 高竞争

6.2 选型决策树

复制代码
你的场景是?
│
├─ 是否需要跨进程? ──→ 是 → 数据库锁 / 分布式锁(Redis/Zookeeper)
│
├─ 并发冲突频繁吗?
│   ├─ 频繁(写多读少) → 悲观锁(synchronized / ReentrantLock)
│   └─ 不频繁(读多写少)→ 乐观锁(CAS / version)
│
├─ 锁持有时间长吗?
│   ├─ 极短(几行代码)  → 自旋锁 / CAS
│   └─ 较长(IO/计算)   → 阻塞锁
│
├─ 需要公平排队吗?
│   ├─ 需要 → ReentrantLock(true)
│   └─ 不需要 → synchronized / ReentrantLock(false)
│
└─ 有嵌套/递归调用吗?
    ├─ 有 → 必须用可重入锁(synchronized / ReentrantLock)
    └─ 无 → 可重入或不可重入均可

6.3 性能对比总结

复制代码
吞吐量排名(高 → 低):
  乐观锁(CAS) > 自旋锁 > 非公平锁 > 公平锁 > 重量级阻塞锁

延迟排名(低 → 高):
  自旋锁 < 乐观锁(CAS) < 非公平锁 ≈ 公平锁 < 重量级阻塞锁

CPU 消耗排名(低 → 高):
  重量级阻塞锁 < 公平锁 < 非公平锁 < 乐观锁(CAS) < 自旋锁

附录:完整依赖

xml 复制代码
<!-- pom.xml -->
<dependencies>
    <!-- JOL:查看对象内存布局 -->
    <dependency>
        <groupId>org.openjdk.jol</groupId>
        <artifactId>jol-core</artifactId>
        <version>0.17</version>
    </dependency>
</dependencies>

📌 核心记住一句话:

  • 悲观/乐观 → 要不要加锁的问题
  • 公平/非公平 → 谁来拿锁的问题
  • 可重入/不可重入 → 同线程能不能重复拿的问题
  • 自旋/阻塞 → 拿不到锁怎么办的问题
  • 偏向/轻量/重量 → JVM 帮你自动选最优策略的问题

相关推荐
caishenzhibiao1 小时前
顺势捕猎者副图 同花顺期货通指标
java·c语言·c#
笨蛋不要掉眼泪2 小时前
RabbitMQ消息队列:SpringAMQP
java·分布式·rabbitmq
Mark_ZP2 小时前
【锁6】AQS (AbstractQueuedSynchronizer) 核心原理详解
java
AI砖家2 小时前
多商户多租户系统架构设计文档(Java版)
java·开发语言·系统架构·多租户·多商户
落苜蓿蓝2 小时前
Java 循环中对象复用导致属性覆盖?从 JVM 内存模型讲解原因
java·jvm·python
weixin_440784112 小时前
Android基础知识汇总
android·java·android studio
布鲁飞丝2 小时前
从零实现富文本编辑器#-浏览器选区与编辑器选区模型同步
java·前端·编辑器
jun_bai3 小时前
使用java安全的移动文件
java
caishenzhibiao3 小时前
市场同步系统 同花顺期货通指标
java·c语言·c#