Java锁机制深度解析:从synchronized到分布式锁 1. 面试官为什么爱问锁机制这个问题几乎出现在90%的中高级Java开发岗位面试中。去年我在美团三轮技术面中被问了两次在阿里终面时又被总监深挖了一次。面试官如此执着的原因很简单锁机制直接反映开发者对并发编程的理解深度。记得2017年做支付系统重构时因为错误使用synchronized导致库存超卖那次事故让我真正明白了锁的选择不能靠运气。现在当面试官抛出这个问题时我首先会观察候选人的回答层次语法层面能否准确说出两种锁的写法差异性能层面是否了解底层实现原理场景层面能否结合业务场景谈选型扩展层面能否延伸到分布式锁的实现接下来我会用实际代码示例和性能测试数据带大家彻底掌握这个必考点。2. synchronized 的深层机制2.1 从字节码看实现原理先看这段简单代码public class SyncDemo { private static int counter 0; public synchronized void increment() { counter; } }用javap -v反编译后关键部分如下public synchronized void increment(); descriptor: ()V flags: ACC_PUBLIC, ACC_SYNCHRONIZED Code: stack2, locals1, args_size1 0: getstatic #2 // Field counter:I 3: iconst_1 4: iadd 5: putstatic #2 // Field counter:I 8: return注意ACC_SYNCHRONIZED标志位这是方法级同步的关键。当线程进入该方法时JVM会隐式获取对象实例的monitor锁退出时自动释放。如果是静态同步方法则锁定Class对象。2.2 锁升级的全过程很多开发者不知道synchronized在JDK1.6后的重大优化——锁升级机制。通过以下代码可以观察整个过程public class LockUpgrade { static final Object lock new Object(); public static void main(String[] args) { // 阶段1无锁 System.out.println(ClassLayout.parseInstance(lock).toPrintable()); // 阶段2偏向锁 synchronized (lock) { System.out.println(ClassLayout.parseInstance(lock).toPrintable()); } // 阶段3轻量级锁 for (int i 0; i 3; i) { new Thread(() - { synchronized (lock) { System.out.println(ClassLayout.parseInstance(lock).toPrintable()); } }).start(); } // 阶段4重量级锁 for (int i 0; i 10; i) { new Thread(() - { synchronized (lock) { try { Thread.sleep(100); } catch (InterruptedException e) { e.printStackTrace(); } } }).start(); } } }使用JOL工具观察对象头变化无锁状态Mark Word最后三位为001偏向锁101存储线程ID轻量级锁00通过CAS自旋获取重量级锁10指向Monitor对象关键点锁升级是不可逆的。一旦升级为重量级锁即使后续没有竞争也不会降级。2.3 使用中的三大陷阱锁粒度问题// 错误示范 - 锁整个方法 public synchronized void processOrder(Order order) { validate(order); deductInventory(); createLog(); } // 正确做法 - 细粒度锁 public void processOrder(Order order) { synchronized(order) { validate(order); } synchronized(inventory) { deductInventory(); } // 日志操作无需同步 }锁对象变更private String lock lock; public void riskyMethod() { synchronized(lock) { lock newLock; // 锁对象被替换 // 后续代码实际上无锁保护 } }死锁场景// 转账死锁案例 public void transfer(Account from, Account to, int amount) { synchronized(from) { synchronized(to) { // 可能产生循环等待 from.debit(amount); to.credit(amount); } } }3. Lock接口的实战解析3.1 核心实现类对比特性ReentrantLockReentrantReadWriteLockStampedLock重入性支持支持不支持公平锁可选可选不可选读写分离不支持支持支持乐观读不支持不支持支持Condition支持支持支持不支持3.2 正确使用模板必须使用try-finally保证锁释放Lock lock new ReentrantLock(); lock.lock(); try { // 临界区代码 } finally { lock.unlock(); // 必须放在finally块 }中断响应示例public boolean transferWithTimeout(Account from, Account to, int amount, long timeout) throws InterruptedException { long stopTime System.nanoTime() timeout; while (true) { if (from.lock.tryLock()) { try { if (to.lock.tryLock()) { try { from.debit(amount); to.credit(amount); return true; } finally { to.lock.unlock(); } } } finally { from.lock.unlock(); } } if (System.nanoTime() stopTime) return false; Thread.sleep(100); } }3.3 性能对比测试用JMH进行基准测试BenchmarkMode(Mode.Throughput) OutputTimeUnit(TimeUnit.MILLISECONDS) public class LockBenchmark { State(Scope.Thread) public static class MyState { public final Lock lock new ReentrantLock(); public int counter 0; } Benchmark public void testSynchronized(MyState state) { synchronized (this) { state.counter; } } Benchmark public void testReentrantLock(MyState state) { state.lock.lock(); try { state.counter; } finally { state.lock.unlock(); } } }测试结果MacBook Pro M1低竞争场景synchronized更快JVM优化高竞争场景ReentrantLock吞吐量高30%读写场景ReadWriteLock比synchronized快5-10倍4. 核心区别的六维对比4.1 实现层面维度synchronizedLock实现方式JVM层面monitorenter/monitorexitJava API层面AQS实现锁状态可见性不可见可通过isLocked()等方法查询中断响应不支持lockInterruptibly()支持4.2 功能特性// Condition的典型使用场景 - 生产者消费者 class BoundedBuffer { final Lock lock new ReentrantLock(); final Condition notFull lock.newCondition(); final Condition notEmpty lock.newCondition(); void put(Object x) throws InterruptedException { lock.lock(); try { while (count items.length) notFull.await(); items[putptr] x; if (putptr items.length) putptr 0; count; notEmpty.signal(); } finally { lock.unlock(); } } }4.3 选型决策树是否需要公平锁是 → ReentrantLock(true)否 → 下一步是否纯写操作是 → synchronized或ReentrantLock否 → 下一步读多写少是 → ReadWriteLock或StampedLock否 → synchronized需要尝试获取锁是 → tryLock()否 → synchronized需要可中断是 → lockInterruptibly()否 → synchronized5. 分布式锁的延伸思考虽然题目问的是单机锁但高级面试常会延伸到分布式场景。可以主动展示知识广度// Redis分布式锁实现模板 public class RedisLock { private Jedis jedis; private String lockKey; public boolean tryLock(String requestId, int expireTime) { String result jedis.set(lockKey, requestId, NX, PX, expireTime); return OK.equals(result); } public boolean unlock(String requestId) { String script if redis.call(get, KEYS[1]) ARGV[1] then return redis.call(del, KEYS[1]) else return 0 end; Object result jedis.eval(script, Collections.singletonList(lockKey), Collections.singletonList(requestId)); return result.equals(1L); } }关键注意事项必须设置过期时间防止死锁加锁value要唯一通常用UUID释放锁要原子操作Lua脚本要考虑锁续期问题看门狗机制6. 高频面试问题集锦synchronized锁方法 vs 锁对象区别锁方法实际锁的是this对象静态方法锁的是Class对象锁对象可以更灵活控制粒度AQS的工作原理核心是state变量CAS维护CLH队列管理线程模板方法设计模式为什么synchronized不是公平锁默认策略减少线程切换可以通过ReentrantLock(true)实现公平公平锁性能通常更低锁消除和锁粗化是什么锁消除JIT对不可能竞争的锁优化锁粗化合并相邻同步块减少开销自旋锁的适用场景临界区代码执行时间短多核CPU环境线程切换成本高于等待成本在准备面试时建议自己用Arthas观察锁竞争情况用JStack分析死锁。我曾在生产环境用这些工具解决过支付系统的锁争用问题实际经验比单纯背理论更有说服力。