1. JMM基础概念与核心问题Java内存模型JMM是理解多线程编程的基石它定义了线程如何与内存交互的规范。在实际面试中面试官常通过以下典型问题考察候选人的理解深度1.1 可见性问题本质当多个线程访问共享变量时一个线程的修改可能对其他线程不可见。这种现象的根本原因在于现代CPU的多级缓存架构每个CPU核心有自己的L1/L2缓存写操作可能暂时停留在store buffer缓存一致性协议如MESI的延迟生效// 典型可见性问题示例 public class VisibilityIssue { private static boolean flag true; public static void main(String[] args) throws InterruptedException { new Thread(() - { while (flag) {} // 可能永远无法退出循环 System.out.println(Thread stopped); }).start(); Thread.sleep(1000); flag false; } }关键点volatile通过内存屏障强制刷新缓存解决可见性问题1.2 指令重排序陷阱编译器和处理器会进行指令优化重排可能破坏程序语义// 双重检查锁中的重排序问题 class Singleton { private static Singleton instance; public static Singleton getInstance() { if (instance null) { // 第一次检查 synchronized (Singleton.class) { if (instance null) { // 第二次检查 instance new Singleton(); // 问题根源 } } } return instance; } }这个经典案例中new Singleton()可能被重排序为分配内存空间将引用指向内存此时instance非null执行构造函数其他线程可能拿到未初始化完成的对象。解决方案是给instance加上volatile修饰。2. 原子性操作深度解析2.1 竞态条件实战分析i这类复合操作的非原子性会导致计数不准public class Counter { private int count; public void increment() { count; // 实际包含read-modify-write三步操作 } }解决方案对比synchronized最直接但性能开销大AtomicIntegerCAS无锁实现适合高并发LongAdder分段计数写多读少场景最优2.2 CAS机制底层原理Atomic类基于Compare-And-Swap实现其核心是public final int getAndIncrement() { return unsafe.getAndAddInt(this, valueOffset, 1); } // HotSpot源码片段 UNSAFE_ENTRY(jboolean, Unsafe_CompareAndSwapInt( JNIEnv *env, jobject unsafe, jobject obj, jlong offset, jint e, jint x)) oop p JNIHandles::resolve(obj); jint* addr (jint*)index_oop_from_field_offset_long(p, offset); return Atomic::cmpxchg(x, addr, e) e; UNSAFE_ENDABA问题解决方案版本号机制AtomicStampedReference标记位控制3. happens-before规则详解3.1 八大规则应用场景程序顺序规则同一线程内的操作按代码顺序锁规则解锁先于后续加锁volatile规则写先于后续读传递性规则A先于BB先于C ⇒ A先于C线程启动规则start()先于线程内任何操作线程终止规则线程内操作先于终止检测中断规则interrupt()调用先于中断检测对象终结规则构造方法先于finalize()3.2 内存屏障实现机制JVM插入内存屏障保证顺序性屏障类型作用对应指令LoadLoad禁止读-读重排序LFENCEStoreStore禁止写-写重排序SFENCELoadStore禁止读-写重排序无直接对应StoreLoad禁止写-读重排序全能屏障MFENCEx86架构下StoreLoad屏障通过lock addl $0,0(%rsp)实现。4. 线程安全设计模式4.1 不可变对象实践// 标准不可变类实现 public final class ImmutablePoint { private final int x; private final int y; public ImmutablePoint(int x, int y) { this.x x; this.y y; } // 只有getter没有setter public int getX() { return x; } public int getY() { return y; } // 返回新对象而非修改现有对象 public ImmutablePoint move(int dx, int dy) { return new ImmutablePoint(x dx, y dy); } }4.2 线程封闭技术栈封闭局部变量天然线程安全ThreadLocal模式public class UserContext { private static final ThreadLocalUser currentUser new ThreadLocal(); public static void set(User user) { currentUser.set(user); } public static User get() { return currentUser.get(); } public static void remove() { currentUser.remove(); // 防止内存泄漏 } }注意事项线程池中使用ThreadLocal必须及时remove()5. 锁优化高级技巧5.1 锁升级过程图解graph LR A[无锁] --|首次访问| B[偏向锁] B --|竞争发生| C[轻量级锁] C --|持续竞争| D[重量级锁] D --|释放后| A实际JVM实现更为复杂涉及偏向锁延迟-XX:BiasedLockingStartupDelay批量重偏向-XX:BiasedLockingBulkRebiasThreshold锁消除-XX:DoEscapeAnalysis5.2 自旋锁优化参数-XX:UseSpinning # 启用自旋JDK6默认 -XX:PreBlockSpin10 # 默认自旋次数 -XX:UseAdaptiveSizePolicy # 自适应自旋现代JVM使用更智能的适应性自旋策略根据历史成功率动态调整。6. 并发容器选型指南6.1 ConcurrentHashMap演进JDK7与JDK8实现对比特性JDK7 Segment分段锁JDK8 CASsynchronized并发度构造时固定动态扩容锁粒度段级别桶级别数据结构数组链表数组链表/红黑树扩容方式段内扩容协助扩容统计size多次尝试锁CounterCell分片计数6.2 BlockingQueue对比队列类型特性适用场景ArrayBlockingQueue固定容量公平锁可选固定资源池LinkedBlockingQueue可选容量双锁队列任务调度系统PriorityBlockingQueue优先级排序紧急任务处理DelayQueue时间调度缓存过期/定时任务SynchronousQueue直接传递线程池任务交接7. 线程池实战调优7.1 参数配置公式// 最优线程数估算 int optimalThreadCount Runtime.getRuntime().availableProcessors() * (1 (waitTime / computeTime)); // 实际配置示例 ExecutorService pool new ThreadPoolExecutor( 4, // corePoolSize 16, // maximumPoolSize 60, TimeUnit.SECONDS, // keepAliveTime new LinkedBlockingQueue(1000), // workQueue new CustomThreadFactory(), // threadFactory new ThreadPoolExecutor.CallerRunsPolicy() // rejectionPolicy );7.2 监控关键指标ThreadPoolExecutor pool (ThreadPoolExecutor) executor; System.out.println(活跃线程数: pool.getActiveCount()); System.out.println(已完成任务: pool.getCompletedTaskCount()); System.out.println(队列大小: pool.getQueue().size()); System.out.println(最大线程数: pool.getLargestPoolSize());推荐使用Micrometer接入Prometheus实现可视化监控。8. 常见面试题精讲8.1 volatile与synchronized区别维度volatilesynchronized原子性仅保证单次读/写原子性保证代码块原子性可见性强制主内存读写通过锁机制保证有序性禁止指令重排序限制临界区内的重排序阻塞非阻塞阻塞适用场景状态标志复合操作8.2 死锁排查实战jstack检测jstack pid | grep -A 10 deadlockArthas诊断thread -b # 直接定位死锁线程预防方案统一锁获取顺序使用tryLock()带超时静态代码分析工具检测9. JMM相关参数调优9.1 内存屏障控制-XX:UseMemBarrier # 启用内存屏障默认开启 -XX:MemBarrierCost50 # 屏障开销估值纳秒9.2 字段重排限制-XX:CompactFields # 启用字段重排序默认true -XX:FieldAllocationStyle0 # 0按类型分组 1按声明顺序10. 最新并发特性展望10.1 虚拟线程Loom项目try (var executor Executors.newVirtualThreadPerTaskExecutor()) { IntStream.range(0, 10_000).forEach(i - { executor.submit(() - { Thread.sleep(Duration.ofSeconds(1)); return i; }); }); }10.2 结构化并发try (var scope new StructuredTaskScope.ShutdownOnFailure()) { FutureString user scope.fork(() - findUser()); FutureInteger order scope.fork(() - fetchOrder()); scope.join(); // 等待所有子任务 scope.throwIfFailed(); // 异常传播 return new Response(user.get(), order.get()); }