尧图网络 高端网站定制 · 原创设计
免费咨询热线
400-888-6620
免费获取方案
Java并发多线程32个核心考点:从原理到面试实战
并发与多线程这块内容几乎是Java面试里绕不过去的一道坎。我面过不少人也被人面过不少次发现一个很普遍的现象很多人背了一堆“线程池七大参数”“synchronized和Lock的区别”但真让他讲清楚一个volatile到底解决了什么问题、为什么HashMap在多线程下会出环就卡壳了。这说明知识点是散的没有串成一条线。这篇内容我打算把并发多线程里最常被问到的32个考点重新梳理一遍不是给你一份背诵清单而是按“为什么会这样设计”“实际会踩什么坑”“面试官到底想听什么”这个逻辑来讲。适合正在准备Java后端面试的朋友也适合工作几年但并发基础不够扎实、想系统补一遍的同学。读完你至少能做到被问到任何一个考点能说出它的原理、适用场景和边界条件而不是只丢一个结论。1. 先把并发和并行的区别讲透这是所有问题的起点很多人一上来就背线程池结果连并发和并行都没分清后面全是空中楼阁。我习惯把这两个词用生活场景来区分讲一次基本就忘不掉。1.1 并发是“交替做”并行是“同时做”并发Concurrency指的是多个任务在同一时间段内交替执行宏观上看像是同时进行但微观上CPU可能只有一个核心在快速切换。并行Parallelism则是多个任务在同一时刻真正同时执行依赖多核CPU。打个比方一个人同时要做饭、回消息、看孩子。如果他一会儿切菜、一会儿回消息、一会儿哄孩子这是并发——他在交替处理。如果他有分身三个自己同时干三件事这才是并行。这个区别为什么重要因为它直接决定了你讨论线程安全时的前提。单核CPU上谈并发本质是时间片轮转带来的上下文切换问题多核上谈并行才涉及真正的内存可见性和指令重排序问题。面试官问“并发和并行的区别”其实是在试探你有没有理解到这一层。1.2 为什么并发会带来线程安全问题线程安全问题的根源可以归结为三个条件同时满足多线程访问、共享资源、存在写操作。只要打破其中一个问题就不存在了。比如每个线程操作自己的局部变量就没有共享或者共享资源只读不写也不会出问题。具体到Java内存模型JMM每个线程有自己的工作内存共享变量存在主内存。线程读变量时会把主内存的值拷贝到工作内存写的时候再刷回主内存。这个过程中如果两个线程各自拷贝了一份各自修改最后刷回时就会互相覆盖这就是可见性问题的来源。我经常用一个例子说明两个线程同时对count0做1000次自增最终结果往往小于2000。原因就是count不是原子操作它包含读取、加一、写回三步中间任何一步都可能被其他线程插入。注意count这种看似简单的操作在字节码层面是四条指令理解这一点是理解原子性问题的关键。1.3 从JMM看三大特性原子性、可见性、有序性Java内存模型定义了三个核心特性所有并发问题都能归到这三类里原子性一个操作不可分割要么全做要么全不做。i不满足synchronized块满足。可见性一个线程修改了共享变量其他线程能立刻看到。volatile和锁都能保证。有序性程序执行的顺序按照代码的先后顺序。编译器和CPU会做指令重排序volatile和happens-before规则能约束。我建议准备面试时把每个考点都往这三个特性上靠。比如问volatile你就答它保证可见性和有序性但不保证原子性问synchronized你就答它三者都保证。这样回答有框架不会乱。2. volatile关键字被问得最多也最容易被答浅volatile几乎是并发面试的必考题但很多人只会说“保证可见性不保证原子性”再往下问就没了。我见过面试官追问“它怎么保证可见性的”“它和内存屏障什么关系”直接把人问懵。2.1 volatile的两层语义第一层是可见性当一个线程修改了volatile变量新值会立即刷回主内存并且会让其他线程工作内存中该变量的缓存失效其他线程读取时必须重新从主内存加载。第二层是禁止指令重排序volatile变量的读写会插入内存屏障阻止编译器和CPU把前后的指令重排。这一点在单例模式的双重检查锁里体现得最典型。public class Singleton { private static volatile Singleton instance; public static Singleton getInstance() { if (instance null) { synchronized (Singleton.class) { if (instance null) { instance new Singleton(); } } } return instance; } }这里instance必须加volatile因为new Singleton()不是原子操作它分三步分配内存、初始化对象、把引用指向内存。如果没有volatile步骤2和3可能重排序导致另一个线程拿到一个还没初始化完的对象。2.2 为什么volatile不保证原子性这是高频追问点。volatile只保证单次读或单次写的原子性但像i这种复合操作它包含读、改、写三步volatile管不住中间被打断。所以volatile修饰的变量做自增结果依然可能出错。要保证原子性得用synchronized、Lock或者AtomicInteger这类原子类。AtomicInteger底层用的是CASCompare And Swap加volatile既保证原子性又保证可见性性能通常比锁好。2.3 volatile的适用场景我总结下来volatile适合两种场景一是状态标志位比如一个boolean running变量控制线程是否继续运行二是双重检查锁的单例。除此之外凡是涉及复合操作的都别用volatile硬扛。提示面试时如果被问“什么时候用volatile”答状态标志和DCL单例再补一句“复合操作场景我会用原子类或锁”基本就稳了。3. synchronized的锁升级过程讲清楚就赢一半synchronized是Java最基础的同步手段但它的实现随着JDK版本演进了很多。现在问synchronized面试官多半想听锁升级无锁、偏向锁、轻量级锁、重量级锁。3.1 对象头与Mark Word要理解锁升级先得知道锁信息存在哪。每个Java对象都有一个对象头其中一部分叫Mark Word用来存储对象的哈希码、GC分代年龄和锁状态。Mark Word在不同锁状态下存储的内容不同这是锁升级的物理基础。在64位JVM上Mark Word通常是64位。无锁状态下存哈希码和分代年龄偏向锁状态下存偏向线程ID轻量级锁状态下存指向栈中锁记录的指针重量级锁状态下存指向Monitor对象的指针。3.2 四种锁状态与升级路径无锁对象刚创建没有任何线程竞争。偏向锁第一个访问的线程会在Mark Word里记录自己的ID之后该线程再进入同步块只需检查ID是否匹配不需要CAS开销极低。适用于“同一个线程反复进入同步块”的场景。轻量级锁当有第二个线程来竞争时偏向锁升级为轻量级锁。线程会在自己栈帧里创建锁记录用CAS尝试把Mark Word指向锁记录。成功就获得锁失败就自旋等待。重量级锁自旋一定次数还拿不到锁就升级为重量级锁线程进入阻塞状态涉及操作系统层面的互斥量开销大但不会空耗CPU。这个升级过程是单向的不会降级。理解这条路径你就能解释为什么“synchronized在低竞争下性能不差”——因为大部分时候它停留在偏向锁或轻量级锁根本没到重量级。3.3 锁升级的面试答法我建议这样组织回答先说锁状态存在Mark Word里再说四种状态和升级触发条件最后补一句“偏向锁在JDK 15之后默认关闭了因为维护成本高、收益有限”。这句话能体现你关注版本演进是加分项。4. 线程池参数、原理、拒绝策略一个都不能少线程池是并发面试的重灾区几乎每场必问。但很多人只会背七大参数问“任务提交后怎么流转”就答不上来。这块我拆成参数、执行流程、拒绝策略、大小设置四部分讲。4.1 七大参数逐个拆解public ThreadPoolExecutor(int corePoolSize, int maximumPoolSize, long keepAliveTime, TimeUnit unit, BlockingQueueRunnable workQueue, ThreadFactory threadFactory, RejectedExecutionHandler handler)corePoolSize核心线程数即使空闲也不会被回收除非开启allowCoreThreadTimeOut。maximumPoolSize最大线程数核心线程满了且队列满了才会创建非核心线程。keepAliveTime非核心线程空闲存活时间。unit时间单位。workQueue任务队列常见有ArrayBlockingQueue、LinkedBlockingQueue、SynchronousQueue。threadFactory线程工厂用来给线程起名排查问题时非常有用。handler拒绝策略队列满且线程数达上限时触发。4.2 任务提交后的完整流转这是面试官最爱追问的点。任务提交后如果当前线程数小于corePoolSize直接创建核心线程执行。如果核心线程已满任务进入workQueue排队。如果队列也满了且线程数小于maximumPoolSize创建非核心线程执行。如果线程数已达maximumPoolSize触发拒绝策略。这里有个容易搞错的点很多人以为先创建到最大线程数再排队其实顺序是先核心、再排队、再非核心、最后拒绝。这个顺序决定了线程池的行为特征。4.3 四种拒绝策略策略行为适用场景AbortPolicy抛RejectedExecutionException默认需要感知任务被拒CallerRunsPolicy调用者线程执行降低提交速度不丢任务DiscardPolicy静默丢弃允许丢任务的场景DiscardOldestPolicy丢弃最老任务再提交新任务优先级高我实际项目里用得最多的是CallerRunsPolicy因为它能形成背压让提交任务的线程慢下来避免任务无限堆积。4.4 线程数怎么设才合理这个问题没有标准答案但有个通用思路CPU密集型任务线程数设为CPU核数加一IO密集型任务线程数可以设为CPU核数乘以1加等待时间与计算时间的比值。我一般会先按经验值设一个然后压测观察CPU利用率和队列积压情况再调。别迷信公式实际负载才是准绳。注意用Executors工厂方法创建线程池要谨慎newFixedThreadPool和newSingleThreadExecutor用的是无界队列任务堆积可能导致OOM。生产环境建议手动new ThreadPoolExecutor。5. AQS与Lock理解ReentrantLock的底层ReentrantLock和AQS是进阶考点能答好这块说明你对并发的理解上了一个台阶。5.1 AQS的核心结构AQSAbstractQueuedSynchronizer内部维护一个volatile int state表示同步状态加一个FIFO的双向队列存放等待线程。线程获取锁失败就包装成Node节点入队然后阻塞等待。state的含义由子类定义ReentrantLock里它表示重入次数Semaphore里它表示可用许可数CountDownLatch里它表示剩余计数。5.2 独占模式与共享模式AQS支持两种模式独占模式如ReentrantLock和共享模式如Semaphore、CountDownLatch。独占模式下同一时刻只有一个线程能持有锁共享模式下多个线程可以同时获取。ReentrantLock的公平与非公平区别在于公平锁在获取锁时会先检查队列里有没有等待更久的线程有就排队非公平锁直接尝试CAS抢锁抢不到再排队。非公平锁吞吐量更高但可能导致线程饥饿。5.3 ReentrantLock与synchronized的对比维度synchronizedReentrantLock实现层面JVM内置JDK代码实现锁释放自动手动unlock需放finally公平性非公平可选公平/非公平可中断不支持支持lockInterruptibly条件变量单一多个Condition超时获取不支持支持tryLock超时我选型的经验是能用synchronized就用它简单不易错需要可中断、超时、多条件变量时才上ReentrantLock。6. CAS与原子类无锁并发的核心CAS是乐观锁的典型实现也是AtomicInteger等原子类的底层。6.1 CAS的原理与ABA问题CAS包含三个值内存地址V、预期值A、新值B。只有当V的当前值等于A时才把V更新为B否则不做任何操作。整个过程由CPU的原子指令保证。CAS有个经典问题叫ABA线程1读到值A线程2把值改成B又改回A线程1的CAS依然成功但它不知道中间变过。解决办法是加版本号AtomicStampedReference就是干这个的。6.2 原子类的分类基本类型AtomicInteger、AtomicLong、AtomicBoolean数组类型AtomicIntegerArray等引用类型AtomicReference、AtomicStampedReference字段更新器AtomicIntegerFieldUpdater等累加器LongAdder、DoubleAdderLongAdder在高并发下比AtomicLong性能好因为它把热点分散到多个Cell最后求和。代价是求和时可能不精确。6.3 CAS的适用与局限CAS适合竞争不激烈的场景无锁、无阻塞、性能好。但竞争激烈时大量线程自旋会空耗CPU这时候锁反而更合适。另外CAS只能保证单个变量的原子性多个变量得用锁或AtomicReference包装。7. 并发容器HashMap的死循环与ConcurrentHashMap的演进集合类的线程安全是高频考点尤其是HashMap和ConcurrentHashMap。7.1 HashMap为什么线程不安全JDK 1.7的HashMap在并发扩容时头插法可能导致链表成环进而让get操作死循环CPU飙到100%。JDK 1.8改成尾插法解决了成环问题但并发put依然可能丢数据。所以HashMap在任何并发场景下都不能用多线程读可以用Collections.unmodifiableMap包装读写并发必须用ConcurrentHashMap。7.2 ConcurrentHashMap的分段锁与CASJDK 1.7的ConcurrentHashMap用分段锁Segment把数据分成多段每段一把锁不同段可以并发写。JDK 1.8抛弃分段锁改用synchronized加CAS加volatile锁的粒度细化到单个桶并发度更高。JDK 1.8的put流程先判断桶是否为空空就用CAS放头节点不为空就synchronized锁住头节点再插入。扩容时支持多线程协助迁移这是1.8的一大优化。7.3 CopyOnWriteArrayList的取舍CopyOnWriteArrayList在写时复制整个数组读操作完全无锁。适合读多写少的场景比如白名单、配置列表。写操作开销大且只能保证最终一致性不适合实时性要求高的场景。8. 内存模型与happens-before面试的隐藏考点happens-before是JMM的核心规则很多面试官用它来考察你对可见性的理解深度。8.1 八条happens-before规则程序顺序规则同一线程内前面的操作happens-before后面的操作。监视器锁规则解锁happens-before后续加锁。volatile变量规则对volatile变量的写happens-before后续的读。线程启动规则Thread.start()happens-before线程内所有操作。线程终止规则线程内所有操作happens-before其他线程检测到该线程终止。线程中断规则interrupt()happens-before被中断线程检测到中断。对象终结规则构造函数完成happens-beforefinalize()开始。传递性A happens-before BB happens-before C则A happens-before C。8.2 happens-before的实际意义如果两个操作满足happens-before那么前一个操作的结果对后一个操作可见且前一个操作的执行顺序在后一个之前。注意happens-before不要求真的按这个顺序执行只要求结果看起来是这样编译器和CPU可以在不违反规则的前提下重排序。理解这一点你就能解释为什么加了volatile之后它前面的普通写也能被后续线程看到——因为volatile写之前的操作happens-before这个写而这个写happens-before后续的读传递下来就都可见了。9. 死锁四个条件与排查手段死锁是并发编程的经典问题面试常问“怎么排查死锁”。9.1 死锁的四个必要条件互斥资源同一时刻只能被一个线程占用。占有并等待线程持有资源的同时等待其他资源。不可剥夺资源不能被强制抢占。循环等待存在线程与资源的环形等待链。破坏任意一个条件就能避免死锁。最常用的是破坏循环等待给所有锁定义全局顺序所有线程按同一顺序加锁。9.2 用jstack排查死锁jstack是排查死锁的利器。先用jps找到Java进程ID再执行jstack pid如果存在死锁输出末尾会有Found one Java-level deadlock并列出涉及的线程和锁信息。jps -l jstack 12345 thread_dump.txt我实际排查时还会结合top -Hp pid找到CPU占用高的线程把线程ID转成十六进制再去jstack输出里搜定位到具体代码行。9.3 避免死锁的实操建议加锁顺序统一这是最有效的办法。用tryLock带超时拿不到就放弃并释放已持有的锁。减少锁的粒度和持有时间。能用无锁结构就用无锁结构。10. 线程生命周期与通信基础但容易答漏线程状态和线程间通信是基础考点但细节多容易答漏。10.1 六种线程状态Java线程有六种状态NEW、RUNNABLE、BLOCKED、WAITING、TIMED_WAITING、TERMINATED。注意RUNNABLE包含了操作系统层面的就绪和运行两种状态Java层面不区分。状态转换的关键点wait()会让线程进入WAITING并释放锁sleep()进入TIMED_WAITING但不释放锁join()底层是wait()yield()只是让出CPU状态还是RUNNABLE。10.2 wait/notify与Conditionwait/notify必须在synchronized块里调用否则抛IllegalMonitorStateException。wait释放锁并等待notify唤醒一个等待线程notifyAll唤醒全部。Condition是Lock体系下的等待通知机制一个Lock可以创建多个Condition实现更精细的唤醒控制。比如生产者消费者模型里可以用两个Condition分别控制生产者和消费者的等待队列。10.3 生产者消费者模型的三种写法用synchronized加wait/notify经典写法简单但唤醒不够精细。用ReentrantLock加Condition可以精确唤醒生产者或消费者。用BlockingQueue最简洁队列本身线程安全天然支持阻塞。我实际项目里首选BlockingQueue代码少、不易错。面试时三种都答一遍体现你理解不同层次的抽象。11. 高频面试题的答法与避坑最后这部分我把几个最容易翻车的面试题单独拎出来讲答法。11.1 “谈谈你对线程安全的理解”别一上来就背定义。我建议按“什么是线程安全、什么情况下会不安全、怎么保证安全”三步走。先说线程安全的定义是多线程访问下程序行为正确再说三个条件多线程、共享资源、写操作最后说保证手段不可变、加锁、无锁、线程封闭。11.2 “ThreadLocal的原理和内存泄漏”ThreadLocal为每个线程维护独立的变量副本底层是Thread里的ThreadLocalMapkey是ThreadLocal的弱引用value是强引用。如果ThreadLocal被回收而线程还活着value就无法被访问却无法回收造成泄漏。解决办法是用完调remove()。提示线程池场景下ThreadLocal必须手动清理否则下一个任务可能读到上一个任务的残留数据这是生产事故的常见来源。11.3 “CountDownLatch和CyclicBarrier的区别”CountDownLatch是一次性的计数减到零后不能重置用于一个线程等待多个线程完成。CyclicBarrier可循环使用用于一组线程互相等待到齐后再继续。前者是“等别人”后者是“大家一起等”。11.4 面试中的表达技巧我面人的时候最怕听到“这个我背过但记不清了”。与其背结论不如讲你实际用过的场景。比如问线程池你就说“我在某个项目里用线程池处理批量任务核心线程设了8队列用了有界队列拒绝策略用CallerRunsPolicy做背压”这种回答比背参数有说服力得多。另外遇到不会的别硬编。可以说“这块我了解得不深但我知道它大概和XX有关回去我会补一下”。诚实加方向感比胡扯强。我自己准备并发面试的时候习惯把每个考点都写成一段能讲两分钟的话然后对着镜子讲一遍卡壳的地方就是没真懂的地方。这个方法笨但管用。并发多线程这东西背是背不出来的得理解到能用自己的话讲清楚才算真的会了。
RELATED

相关推荐

中介效应检验方法详解:从逐步回归到Sobel检验与Bootstrap替代

中介效应检验方法详解:从逐步回归到Sobel检验与Bootstrap替代

简介:面向开展中介效应检验的实证研究者,这份DOCX文档系统整理了中介效应的核心概念与常见检验路径:逐步检验法、Sobel检验与Bootstrap检验。内容从变量中心化处理讲起,逐一给出回归方程、判定依据和STATA操作命令,并说…

📅 2026/10/9 15:15:36
Python记账可视化:从CSV到可交互消费洞察仪表盘

Python记账可视化:从CSV到可交互消费洞察仪表盘

简介:本资源是一份面向Python初学者与个人财务爱好者的数据分析实践项目,聚焦日常记账数据的自动化处理与消费行为可视化,帮助用户快速掌握用代码理清收支结构、识别消费偏好并支撑理性决策。压缩包共3个文件:1个Excel&#xff08…

📅 2026/10/9 15:15:36
第 36 章 · 综合项目一:最小二乘拟合

第 36 章 · 综合项目一:最小二乘拟合

把全书知识串起来的第一个实战项目:用 Eigen 做最小二乘直线拟合。这是数据科学、机器学习的入门基础,也是 Eigen 的典型应用。36.1 问题:拟合一条直线 假设你有一批实验数据点 (x, y): (0, 1.1), (1, 3.0), (2, 5.2), (3, 6.9), …

📅 2026/10/9 15:15:36
MORE NEWS

更多资讯

📰

MySQL农历数据库表结构设计与批量导入实践

简介:这份面向后端开发与业务系统的MySQL农历数据库覆盖1970—2100年共131年数据,包含农历日期、闰月、24节气、星期及法定假日等,可解决农历转换结果不一致、节假日信息不全的问题。压缩包仅2.56MB,共3个文件,其中2个…

📰

WPTools v6.29.1:WordPress静态安全扫描CLI工具详解

简介:WPTools v6.29.1 Standard Edition 是一套面向 Delphi/CBuilder 开发者的专业富文本编辑控件库,适用于 Windows 平台桌面应用开发,尤其适合需深度定制 RTF 编辑、打印与文档渲染功能的中高级开发者。资源包共含 497 个文件,主…

📰

伴随灵敏度分析驱动肿瘤时空放疗优化:Matlab实现全解析

前两年我接手了一个挺让人头疼的课题:肿瘤放疗计划优化。科室那边希望我不仅仅把“总剂量”算出来,而是能根据肿瘤的生长动力学,把空间上怎么照射、时间上怎么分割,一起交给模型去优化。折腾了几个月,真正让我把性能提…

📰

双目视觉工程实战:从标定到深度图生成与点云重建的全流程解析

简介:面向计算机视觉学习与开发的双目立体视觉系统资源包,围绕立体匹配、视差计算、深度图生成、点云重建、目标检测与跟踪、实时视频处理及相机标定与校正等关键技术展开,适用于机器人导航、自动驾驶等场景的算法验证与原型搭建。资源共132个…

📰

数据库系统工程师能力图谱:从2020真题解构底层核心能力

简介:本资源为2020年全国计算机技术与软件专业技术资格(水平)考试——数据库系统工程师科目上午真题及权威答案解析,专为备考软考中级职称的IT从业者、高校相关专业学生及数据库方向初学者设计,助力系统梳理计算机基础…

📰

基于PCA9422与MK51的嵌入式系统电源管理方案设计

最近在调一块带外部电源管理芯片的板子,核心器件组合是 PCA9422 这颗 PMIC 和 MK51DN512CLQ10 这颗 MCU。MK51DN512CLQ10 是 Kinetis 家族里的 K5 系列,Cortex-M4F 内核,512KB Flash,100 pin LQFP 封装,资源对中高端工…

TODAY

今日更新

THIS WEEK

本周精选

THIS MONTH

本月热门

读完文章,想聊聊您的网站?

告诉我们您的行业与需求,资深顾问一对一梳理方案与报价,全程免费。

📞 💬