1. 为什么synchronized是Java面试的必考题在Java技术面试中synchronized关键字出现的频率几乎和自我介绍一样高。作为Java并发编程的基石它不仅是线程安全的保障手段更是考察候选人底层理解能力的试金石。我经历过上百场技术面试发现90%的面试官都会以不同形式考察synchronized这背后有三个深层原因首先synchronized涉及JVM底层实现能直接反映候选人对Java运行机制的理解深度。当面试官问你对象头里存储了什么信息时他实际上在考察你是否了解Java对象在内存中的布局结构。其次锁升级过程是并发编程的核心知识点。从偏向锁到重量级锁的转换过程涉及到操作系统内核态切换、线程状态变更等关键概念这些内容能全面评估候选人的知识体系完整性。最后synchronized的使用场景和优化技巧直接关系到系统性能。在高并发场景下不当的锁使用会导致吞吐量急剧下降而优秀的开发者必须掌握如何平衡线程安全与性能的关系。2. synchronized的底层实现机制2.1 Java对象头与Monitor每个Java对象在堆内存中都由对象头、实例数据和对齐填充三部分组成。其中对象头包含两类信息Mark Word存储对象的哈希码、GC分代年龄、锁状态标志等Klass Pointer指向对象元数据的指针在32位JVM中Mark Word的结构如下表所示锁状态25bit4bit1bit(偏向锁)2bit(锁标志)无锁对象的hashCode对象分代年龄001偏向锁线程IDepoch对象分代年龄101轻量级锁指向栈中锁记录的指针--00重量级锁指向Monitor的指针--10GC标记---11当线程进入synchronized代码块时JVM会根据竞争情况选择不同的锁实现方式。Monitor管程是重量级锁的核心组件它包含以下关键字段_owner指向持有锁的线程_EntryList存储阻塞等待锁的线程_WaitSet存储调用wait()方法的线程2.2 锁升级的全过程现代JVM采用逐步升级的锁策略来平衡性能与开销偏向锁阶段无竞争场景 当第一个线程访问同步块时通过CAS操作将Mark Word中的线程ID替换为当前线程ID。此时锁进入偏向模式后续该线程进入同步块时只需检查线程ID是否匹配无需额外同步操作。轻量级锁阶段轻度竞争 当第二个线程尝试获取锁时偏向锁升级为轻量级锁。JVM会在当前线程的栈帧中创建锁记录(Lock Record)将对象头的Mark Word复制到锁记录中然后尝试用CAS将对象头的Mark Word替换为指向锁记录的指针。重量级锁阶段激烈竞争 如果CAS操作失败说明存在多个线程竞争此时会升级为重量级锁。JVM会向操作系统申请Monitor未获取锁的线程会被阻塞并放入_EntryList这涉及到用户态到内核态的切换开销最大。重要提示在JDK 15中偏向锁已被标记为废弃(JEP 374)因为现代多核处理器环境下偏向锁带来的收益往往小于维护成本。3. synchronized的实战应用与优化3.1 四种同步作用域对比根据修饰对象的不同synchronized有四种使用方式实例方法同步public synchronized void method() { // 锁对象是当前实例(this) }静态方法同步public static synchronized void staticMethod() { // 锁对象是当前类的Class对象 }代码块同步实例对象public void method() { synchronized(this) { // 锁对象是当前实例 } }代码块同步任意对象private final Object lock new Object(); public void method() { synchronized(lock) { // 锁对象是自定义的lock对象 } }3.2 锁优化的七个关键技巧减小锁粒度将大同步块拆分为多个小同步块典型例子是ConcurrentHashMap的分段锁设计。降低锁竞争使用ThreadLocal或副本变量避免共享资源竞争。替换锁机制在适当场景用ReentrantLock代替synchronized它提供更灵活的锁获取方式。避免嵌套锁防止多个线程以不同顺序获取相同的锁集合导致死锁。锁分离策略读写锁分离如ReadWriteLock的实现。锁粗化对连续多次加锁-解锁操作合并为单次锁操作。无锁编程使用CAS操作如AtomicInteger替代锁。4. 高频面试问题深度解析4.1 synchronized与ReentrantLock的对比特性synchronizedReentrantLock实现机制JVM层面实现JDK层面实现(AQS)锁获取方式自动获取和释放需要显式调用lock()/unlock()可中断性不支持支持lockInterruptibly()公平锁非公平可配置公平/非公平条件变量只能有一个等待队列可创建多个Condition性能JDK6后性能接近高竞争下略优锁绑定多个条件不支持支持4.2 锁的内存语义与happens-before关系synchronized建立的内存屏障保证了以下happens-before关系解锁操作happens-before后续对同一锁的加锁操作进入同步块前会清空工作内存退出时会将变量刷新回主内存这种内存可见性保证是synchronized线程安全的核心基础。考虑以下代码// 线程A synchronized(lock) { x 1; // 操作1 } // 线程B synchronized(lock) { System.out.println(x); // 操作2 }根据happens-before原则操作1的结果对操作2一定可见。5. 生产环境中的锁问题诊断5.1 常见锁问题识别死锁检测使用jstack获取线程转储查找deadlock关键词jstack -l pid thread_dump.txt锁竞争分析通过JMC(Java Mission Control)的锁分析功能可以观察到锁等待时间持有锁的线程竞争最激烈的锁性能指标监控锁获取平均时间每秒锁获取次数等待线程数5.2 锁优化实战案例某电商平台在秒杀场景下遇到性能瓶颈原始代码如下public class SeckillService { private static int stock 1000; public synchronized boolean seckill() { if(stock 0) { stock--; return true; } return false; } }优化方案使用AtomicInteger替代synchronized引入分段锁减少竞争前置库存校验减少锁持有时间最终实现public class OptimizedSeckillService { private final AtomicInteger stock new AtomicInteger(1000); private final StampedLock lock new StampedLock(); public boolean seckill() { // 乐观读 long stamp lock.tryOptimisticRead(); int current stock.get(); if(current 0) return false; if(!lock.validate(stamp)) { // 升级为悲观锁 stamp lock.readLock(); try { current stock.get(); } finally { lock.unlockRead(stamp); } } // CAS更新 return stock.compareAndSet(current, current - 1); } }优化后QPS从200提升到5000关键是通过锁降级和CAS操作减少了线程阻塞时间。