Java 锁核心知识 + 高频面试题

一Java 锁核心基础知识

锁是多线程并发编程中,用于控制多个线程对共享资源互斥访问的机制,保证同一时刻只有一个线程操作数据,从而避免并发安全问题。

image

image

image

1. 锁的核心分类(按维度划分)

分类维度 具体类型 核心特点
锁的状态 偏向锁 / 轻量级锁 / 重量级锁(synchronized 升级路径) 从无锁→偏向锁→轻量级锁→重量级锁,按需升级,减少锁竞争开销
锁的获取范围 悲观锁 / 乐观锁 悲观锁:认为一定会冲突(如 synchronized);乐观锁:认为大概率不冲突(如 CAS)
锁的释放机制 可重入锁 / 不可重入锁 可重入:同一线程可重复获取同一把锁(如 ReentrantLock);不可重入:反之
锁的共享粒度 独占锁 / 共享锁 独占锁:同一时间仅一个线程获取(如 ReentrantLock);共享锁:多线程可共享(如 ReentrantReadWriteLock 读锁)
锁的公平性 公平锁 / 非公平锁 公平锁:按排队顺序获取锁;非公平锁:可插队(默认,性能更高)

Java 中实现锁的核心方式

Java 中实现锁主要分为两大方向:JVM 内置锁(无需手动实现,直接使用)和自定义锁(基于 AQS / 基础机制手动实现)

面试题 1:synchronized 的三种使用方式及底层原理

synchronized是 Java 内置的互斥锁(悲观锁),保证原子性、可见性和有序性,有三种使用形式:

1.修饰实例方法:锁对象是当前类的实例对象(this)

2.修饰静态方法:锁对象是当前类的Class 对象(全局锁)

3.修饰代码块:锁对象可自定义(实例对象 / Class 对象 / 任意对象)

底层原理:

  • JVM 通过监视器锁(Monitor) 实现,依赖操作系统的mutex lock(互斥锁)

  • 实例方法 / 代码块:通过对象头的Mark Word存储锁状态

  • 静态方法:锁存储在 Class 对象的 Mark Word 中

  • 锁升级流程:无锁 → 偏向锁 → 轻量级锁 → 重量级锁(优化性能)

public class SynchronizedDemo {
    // 1. 修饰实例方法:锁是SynchronizedDemo的实例对象
    public synchronized void instanceMethod() {
        System.out.println("实例方法锁:" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000); // 模拟业务逻辑
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 2. 修饰静态方法:锁是SynchronizedDemo.class(全局锁)
    public static synchronized void staticMethod() {
        System.out.println("静态方法锁:" + Thread.currentThread().getName());
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    // 3. 修饰代码块:自定义锁对象
    private final Object lock = new Object();
    public void codeBlockMethod() {
        synchronized (lock) { // 锁对象:lock实例
            System.out.println("代码块锁:" + Thread.currentThread().getName());
            try {
                Thread.sleep(1000);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }
    }

    // 测试
    public static void main(String[] args) {
        SynchronizedDemo demo1 = new SynchronizedDemo();
        SynchronizedDemo demo2 = new SynchronizedDemo();

        // 实例方法锁:demo1和demo2是不同对象,不互斥
        new Thread(demo1::instanceMethod, "线程1").start();
        new Thread(demo2::instanceMethod, "线程2").start();

        // 静态方法锁:全局唯一,互斥
        new Thread(SynchronizedDemo::staticMethod, "线程3").start();
        new Thread(SynchronizedDemo::staticMethod, "线程4").start();

        // 代码块锁:lock是demo1的成员,demo1的调用互斥,demo2不影响
        new Thread(demo1::codeBlockMethod, "线程5").start();
        new Thread(demo2::codeBlockMethod, "线程6").start();
    }
}

面试题 2:synchronized 和 Lock 的区别

维度 synchronized Lock(如 ReentrantLock)
锁获取方式 自动获取 / 释放(出作用域自动释放) 手动获取(lock ())/ 释放(unlock ())
锁类型 非公平锁(默认) 支持公平锁 / 非公平锁(构造方法指定)
中断响应 不支持中断 支持中断(lockInterruptibly ())
超时获取 不支持 支持(tryLock (long time, TimeUnit))
条件变量 单一条件队列 支持多个 Condition(newCondition ())
底层实现 JVM 层面(监视器锁) 底层基于 AQS(AbstractQueuedSynchronizer)

使用场景:

  • synchronized:适合简单场景(如单方法同步),代码简洁,无需手动释放

  • Lock:适合复杂场景(如超时获取、公平锁、多条件唤醒),灵活性更高


import java.util.concurrent.locks.Condition;
import java.util.concurrent.locks.ReentrantLock;

public class LockVsSynchronizedDemo {
    // Lock示例:公平锁 + 多条件唤醒
    private static final ReentrantLock fairLock = new ReentrantLock(true); // 公平锁
    private static final Condition condition1 = fairLock.newCondition();
    private static final Condition condition2 = fairLock.newCondition();

    public static void lockDemo() throws InterruptedException {
        fairLock.lock(); // 手动加锁
        try {
            System.out.println(Thread.currentThread().getName() + "获取锁");
            condition1.await(); // 等待条件1
            System.out.println(Thread.currentThread().getName() + "被唤醒");
        } finally {
            fairLock.unlock(); // 必须在finally中释放,避免死锁
        }
    }

    // 测试Lock的超时获取
    public static void tryLockDemo() {
        try {
            // 尝试1秒内获取锁,获取失败则返回false
            if (fairLock.tryLock(1, java.util.concurrent.TimeUnit.SECONDS)) {
                try {
                    System.out.println(Thread.currentThread().getName() + "超时获取锁成功");
                } finally {
                    fairLock.unlock();
                }
            } else {
                System.out.println(Thread.currentThread().getName() + "超时获取锁失败");
            }
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // 测试超时获取锁
        new Thread(LockVsSynchronizedDemo::tryLockDemo, "线程A").start();
        fairLock.lock(); // 主线程先获取锁
        Thread.sleep(2000); // 持有2秒,让线程A超时
        fairLock.unlock();

        // 测试多条件唤醒
        new Thread(LockVsSynchronizedDemo::lockDemo, "线程B").start();
        Thread.sleep(1000);
        fairLock.lock();
        condition1.signal(); // 唤醒等待condition1的线程B
        fairLock.unlock();
    }
}

面试题 3:什么是死锁?如何避免和排查?

死锁定义: 多个线程互相持有对方需要的锁,且都不释放,导致所有线程永久阻塞。

死锁产生的 4 个必要条件

  • 互斥:锁只能被一个线程持有

  • 占有且等待:线程持有一个锁,同时等待另一个锁

  • 不可抢占:线程持有的锁不能被其他线程强制抢占

  • 循环等待:线程 A 等待线程 B 的锁,线程 B 等待线程 A 的锁


public class DeadLockDemo {
    // 定义两个锁对象
    private static final Object lockA = new Object();
    private static final Object lockB = new Object();

    // 线程1:先拿lockA,再拿lockB
    private static void thread1() {
        new Thread(() -> {
            synchronized (lockA) {
                System.out.println(Thread.currentThread().getName() + "持有lockA,等待lockB");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lockB) {
                    System.out.println(Thread.currentThread().getName() + "获取lockB,执行完成");
                }
            }
        }, "线程1").start();
    }

    // 线程2:先拿lockB,再拿lockA(导致循环等待)
    private static void thread2() {
        new Thread(() -> {
            synchronized (lockB) {
                System.out.println(Thread.currentThread().getName() + "持有lockB,等待lockA");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lockA) {
                    System.out.println(Thread.currentThread().getName() + "获取lockA,执行完成");
                }
            }
        }, "线程2").start();
    }

    // 修复死锁:统一锁的获取顺序(破坏循环等待)
    private static void fixedThread1() {
        new Thread(() -> {
            synchronized (lockA) { // 先拿lockA
                System.out.println(Thread.currentThread().getName() + "持有lockA,等待lockB");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lockB) {
                    System.out.println(Thread.currentThread().getName() + "获取lockB,执行完成");
                }
            }
        }, "修复线程1").start();
    }

    private static void fixedThread2() {
        new Thread(() -> {
            synchronized (lockA) { // 统一先拿lockA,再拿lockB
                System.out.println(Thread.currentThread().getName() + "持有lockA,等待lockB");
                try {
                    Thread.sleep(1000);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
                synchronized (lockB) {
                    System.out.println(Thread.currentThread().getName() + "获取lockB,执行完成");
                }
            }
        }, "修复线程2").start();
    }

    public static void main(String[] args) {
        // 产生死锁
        // thread1();
        // thread2();

        // 修复死锁
        fixedThread1();
        fixedThread2();
    }
}


面试题 4:volatile 和锁的区别

image

public class VolatileVsLockDemo {
    // volatile变量:保证可见性,但不保证原子性
    private static volatile int count = 0;
    // 锁对象:保证原子性
    private static final Object lock = new Object();

    // 测试volatile的原子性问题
    private static void volatileTest() {
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                count++; // 非原子操作:读取→自增→写入
            }).start();
        }
    }

    // 测试锁保证原子性
    private static void lockTest() {
        for (int i = 0; i < 1000; i++) {
            new Thread(() -> {
                synchronized (lock) {
                    count++; // 原子操作
                }
            }).start();
        }
    }

    public static void main(String[] args) throws InterruptedException {
        // volatileTest();
        lockTest();
        Thread.sleep(2000);
        System.out.println("最终count值:" + count);
        // volatileTest执行结果:小于1000(原子性问题)
        // lockTest执行结果:等于1000(原子性保证)
    }
}

posted @ 2026-03-14 16:30  白鹿为溪  阅读(79)  评论(0)    收藏  举报