尧图建网站 尧图建网站 YAOTU WEB BUILD 免费咨询
ARTICLE DETAIL

资讯详情

深耕网站建设与建站编程的一线实战洞察。

Java多线程核心知识与面试高频问题解析

Java多线程核心知识与面试高频问题解析 1. 为什么Java多线程面试总让人头疼每次面试前刷Java多线程题目时是不是总有种一看就会一写就废的感觉我当年准备面试时光是synchronized和volatile的区别就背了七八遍结果被面试官一个实际场景题问得哑口无言。后来做了面试官才发现90%的候选人都卡在以下几个典型问题上死记硬背概念但不会解决实际问题比如让你设计个线程安全的计数器知道锁但说不清锁升级过程从偏向锁到重量级锁的完整流程用过线程池但说不清核心参数的关系corePoolSize和maxPoolSize如何配合工作听说过JUC包但连最基础的AQS原理都解释不清提示真正懂多线程的开发者应该能三句话说清楚synchronized和ReentrantLock的本质区别提示从实现机制、功能特性和使用场景三个维度2. 多线程核心知识体系拆解2.1 线程基础必须掌握的三个维度内存模型层面JMM的happens-before原则写代码时最常违反的就是程序顺序规则volatile的可见性实现原理通过内存屏障禁止指令重排序指令重排序的典型caseDCL单例为什么要加volatile线程控制层面// 错误示范直接继承Thread的局限性 class MyThread extends Thread { public void run() { // 业务代码 } } // 正确做法实现Runnable接口 class MyTask implements Runnable { Override public void run() { // 业务代码 } }状态转换层面NEW - RUNNABLEstart()方法调用时RUNNABLE - BLOCKEDsynchronized锁竞争时RUNNABLE - WAITINGwait()/notify()调用时RUNNABLE - TIMED_WAITINGsleep(ms)调用时2.2 锁机制的深度解析synchronized的锁升级全过程无锁状态新创建的对象偏向锁第一个线程访问时通过CAS设置ThreadID轻量级锁有竞争但未膨胀时自旋尝试获取锁重量级锁竞争激烈时向OS申请mutexReentrantLock的实战技巧Lock lock new ReentrantLock(); Condition condition lock.newCondition(); void demo() throws InterruptedException { lock.lock(); // 建议放在try外部 try { while(条件不满足) { condition.await(); // 释放锁并等待 } // 业务处理 condition.signal(); } finally { lock.unlock(); // 必须放在finally } }3. JUC并发工具包实战指南3.1 AQS的实现精髓AbstractQueuedSynchronizer的核心设计state变量通过CAS操作控制同步状态CLH队列用双向链表实现的等待队列模板方法模式tryAcquire/tryRelease由子类实现CountDownLatch典型场景// 模拟并行任务处理 CountDownLatch latch new CountDownLatch(3); ExecutorService executor Executors.newFixedThreadPool(3); for (int i 0; i 3; i) { executor.execute(() - { try { // 模拟任务执行 Thread.sleep(1000); } finally { latch.countDown(); } }); } latch.await(); // 阻塞直到所有任务完成 System.out.println(所有任务执行完毕);3.2 线程池的七个核心参数corePoolSize常驻核心线程数不会被回收maximumPoolSize最大线程数应急创建的临时线程keepAliveTime临时线程空闲存活时间unit时间单位workQueue任务队列ArrayBlockingQueue/LinkedBlockingQueuethreadFactory线程创建工厂handler拒绝策略AbortPolicy/CallerRunsPolicy等配置公式CPU密集型corePoolSize CPU核数 1IO密集型corePoolSize CPU核数 * 24. 高频面试题深度剖析4.1 CAS的ABA问题解决方案问题复现步骤线程1读取变量值为A线程2修改变量A→B→A线程1再次比较时发现仍是A误认为未被修改过解决方案对比表方案实现方式优缺点版本号AtomicStampedReference精确但性能开销大布尔标记AtomicMarkableReference轻量但可能冲突4.2 死锁的四个必要条件及破解必要条件互斥条件请求与保持不剥夺条件循环等待预防方案顺序加锁法统一获取锁的顺序超时放弃tryLock(timeout)银行家算法预先计算安全序列5. 性能优化实战技巧5.1 线程上下文切换的成本测量// 测试代码示例 long start System.nanoTime(); for (int i 0; i 1000000; i) { Thread.yield(); // 主动让出CPU } long duration System.nanoTime() - start; System.out.println(平均每次切换耗时 duration/1000000 ns);实测数据普通上下文切换1-2微秒跨核上下文切换3-5微秒5.2 锁粒度的优化策略错误案例public synchronized void process() { // 方法级锁 // 读操作 // 写操作 }优化方案public void process() { // 无锁读操作 synchronized(this) { // 细化锁范围 // 写操作 } }6. 避坑指南与实战经验ThreadLocal的内存泄漏必须配合try-finally清理finally中调用remove()使用static修饰避免每次创建新实例Future.get()的阻塞陷阱Future? future executor.submit(task); try { future.get(500, TimeUnit.MILLISECONDS); // 必须设置超时 } catch (TimeoutException e) { future.cancel(true); // 中断任务 }ConcurrentHashMap的size()误区不要用size()做精确控制因为它是估算值业务判断应该用mappingCount()方法我在美团带团队时曾遇到一个典型case某核心服务使用FixedThreadPool处理请求在流量突增时大量请求堆积导致OOM。后来改用自定义的RejectedExecutionHandler在拒绝时自动降级返回兜底数据配合动态线程池参数调整完美解决了问题。这告诉我们多线程问题往往不是技术难点而是设计思维和工程经验的体现。
返回列表