✨ 专栏:Java零基础全套入门连载教程
📌 简介:并发编程是Java后端进阶分水岭、面试压轴重难点、高性能项目核心基石。日常高并发接口、异步任务、批量处理、微服务调度、中间件底层,全部依赖并发思想实现。绝大多数线上CPU飙高、死锁、内存溢出、数据脏读、接口超时问题,根源都是并发基础不扎实。本文万字零基础拆解线程核心、线程生命周期、锁体系、volatile、CAS无锁机制、AQS底层原理、七大线程池参数、四种拒绝策略、JUC并发容器、并发工具类,配套可运行实战代码、底层原理、避坑方案、满分面试话术,一站式吃透Java并发全集!
🔖 标签:Java并发,多线程,锁机制,volatile,CAS,AQS,线程池,JUC并发容器,Java面试
一、并发基础核心概念(开篇必背)
1.1 进程与线程(面试高频区分)
满分标准答案:
进程:操作系统资源分配的最小单位,独立占用内存、CPU资源,进程之间数据隔离、互不干扰,例如运行中的JVM、浏览器、微信。
线程:进程内的执行单元,是CPU调度的最小单位,一个进程包含多个线程,线程共享进程堆内存与方法区资源,私有栈内存,切换开销极小。
核心区别金句:进程是资源分配单位,线程是执行调度单位;进程开销大、隔离性强,线程开销小、资源共享。
1.2 并发与并行
并发(Concurrent):单核CPU快速轮换执行多个线程,宏观同时执行,微观交替执行,解决任务阻塞、提升CPU利用率。
并行(Parallel):多核CPU同时执行多个线程,宏观微观均同时执行,真正意义上的多任务同时处理,提升任务执行效率。
1.3 多线程三大核心问题
多线程并发之所以出现线程安全问题,根源来自三点,所有并发Bug均围绕这三大问题展开:
可见性:一个线程修改共享变量,其他线程无法及时感知;
原子性:多线程操作共享变量,操作不具备不可分割性,出现数据覆盖、错乱;
有序性:CPU与编译器为优化性能,会进行指令重排,打乱代码执行顺序,引发逻辑异常。
核心结论:volatile解决可见性、有序性;锁与CAS解决原子性,三者组合覆盖所有并发安全场景。
二、线程核心详解与生命周期
2.1 线程四种创建方式(面试必问)
2.1.1 继承Thread类
优点:写法简单、直接启动;缺点:单继承限制,无法继承其他类,无返回值。
/** * 线程创建方式1:继承Thread类 * 特点:写法简单,无返回值,受单继承限制 */classMyThreadextendsThread{@Overridepublicvoidrun(){System.out.println("线程执行:"+Thread.currentThread().getName());}}2.1.2 实现Runnable接口
优点:避免单继承限制、线程任务可复用;缺点:无返回值、无法抛出异常。
/** * 线程创建方式2:实现Runnable接口 * 特点:规避单继承限制,任务可复用,无返回值 */classMyRunnableimplementsRunnable{@Overridepublicvoidrun(){System.out.println("Runnable线程执行");}}2.1.3 实现Callable接口(有返回值)
优点:有返回值、可抛异常,支持Future获取执行结果;缺点:写法相对繁琐,需配合FutureTask使用。
/** * 线程创建方式3:实现Callable接口 * 特点:支持返回值、可抛出异常,需配合FutureTask使用 */importjava.util.concurrent.Callable;classMyCallableimplementsCallable<Integer>{@OverridepublicIntegercall()throwsException{System.out.println("Callable线程执行,计算结果");return666;}}2.1.4 线程池创建(企业开发首选)
企业开发禁止手动创建线程,统一使用线程池,可复用线程、控制并发数、减少创建销毁开销,后文详细拆解。
2.2 线程六大生命周期(满分背诵)
JDK官方定义线程六大状态,全部位于Thread.State枚举中:
NEW(新建):线程对象创建完成,未调用start(),未启动;
RUNNABLE(就绪/运行):包含就绪、运行两种状态,调用start()后等待CPU调度,或正在执行;
BLOCKED(阻塞):等待获取synchronized内置锁,未拿到锁进入阻塞队列;
WAITING(无限等待):无时限等待,需手动唤醒,调用wait()、join()进入;
TIMED_WAITING(限时等待):限时等待,时间到自动唤醒,调用sleep()、wait(time)进入;
TERMINATED(终止):线程代码执行完毕或异常终止,生命周期结束。
2.3 高频线程方法区别(面试必考)
2.3.1 sleep() vs wait()
| 对比维度 | sleep() | wait() |
|---|---|---|
| 所属类 | Thread类静态方法 | Object类成员方法 |
| 锁释放 | 不释放锁,抱着锁休眠 | 释放锁,其他线程可竞争锁 |
| 唤醒方式 | 时间到自动唤醒 | 需notify()/notifyAll()手动唤醒 |
| 使用场景 | 单纯延时休眠 | 线程间通信、资源等待 |
2.3.2 yield() vs join()
yield():线程主动让出CPU执行权,回到就绪状态,不释放锁,仅让步不阻塞;
join():主线程等待子线程执行完毕,再继续执行,实现线程执行顺序控制。
三、Java锁机制全集(同步锁&显式锁)
3.1 锁核心分类
Java锁体系分为两大核心:synchronized内置锁(JVM层面)、Lock显式锁(代码层面),适配不同并发场景。
3.2 synchronized 内置锁(重点)
核心特性:可重入、独占式、自动加锁解锁、无需手动释放、异常自动释放锁、底层依赖JVM实现。
3.2.1 三种使用方式
修饰普通方法:锁当前对象this;
修饰静态方法:锁当前类Class对象,全局唯一;
修饰代码块:锁自定义对象,粒度最细、性能最优。
3.2.2 JDK1.6锁升级机制(面试压轴)
为优化synchronized性能,JDK1.6引入锁升级机制,锁状态单向不可逆升级:
无锁:初始状态,无线程竞争;
偏向锁:单线程无竞争场景,偏向首个获取锁的线程,无加锁开销;
轻量级锁:多线程轻微竞争,基于CAS自旋尝试获取锁,无阻塞;
重量级锁:线程激烈竞争,CAS自旋失败,线程进入阻塞队列,依赖操作系统调度,开销最大。
核心结论:synchronized不再是重型锁,无竞争场景下性能媲美Lock,低并发场景优先使用。
3.3 Lock 显式锁(ReentrantLock)
核心特性:可重入、可中断、可超时、支持公平锁/非公平锁、手动加锁解锁、粒度灵活。
/** * ReentrantLock 显式锁标准使用案例 * 规范:必须通过try-finally确保锁释放,避免异常死锁 * 支持公平/非公平锁、可中断、超时加锁 */importjava.util.concurrent.locks.ReentrantLock;publicclassLockDemo{// 传true为公平锁,默认非公平锁privatestaticfinalReentrantLockLOCK=newReentrantLock(true);publicstaticvoidmain(String[]args){LOCK.lock();try{// 线程安全临界区代码System.out.println("显式锁执行临界区逻辑");}finally{// 最终释放锁,杜绝死锁LOCK.unlock();}}}3.4 synchronized vs ReentrantLock 终极对比
| 对比维度 | synchronized | ReentrantLock |
|---|---|---|
| 实现层面 | JVM底层原生实现 | JDK代码层面实现(AQS) |
| 加锁解锁 | 自动加锁、自动释放 | 手动lock()、unlock() |
| 锁类型 | 非公平锁、不可中断 | 支持公平/非公平、可中断、可超时 |
| 性能 | JDK1.6后优化,低并发性能优秀 | 高并发、复杂场景性能更优 |
| 适用场景 | 简单同步、低并发场景 | 高并发、锁竞争激烈、需精细化控制 |
3.5 读写锁 ReentrantReadWriteLock
核心思想:读写分离、读多写少优化
读锁:共享锁,多线程可同时读,提升查询吞吐量;
写锁:独占锁,写操作独占资源,保证数据一致性;
互斥规则:读读共享、读写互斥、写写互斥。
适用场景:读多写少业务,如配置缓存、商品数据、字典数据。
四、volatile 关键字(轻量级并发核心)
4.1 volatile 三大核心作用(满分背诵)
volatile是轻量级同步关键字,不依赖锁机制,性能极高,核心能力三点:
保证可见性:一个线程修改volatile变量,立即刷新主内存,其他线程实时感知最新值;
禁止指令重排:通过内存屏障,禁止编译器、CPU对volatile变量前后指令重排序;
不保证原子性:仅保证单变量读写安全,不支持复合操作(i++、i+=1)。
4.2 为什么不保证原子性?
i++ 操作分为三步:读取值 → 运算+1 → 赋值,volatile只能保证每一步可见有序,无法保证三步操作整体不可分割,多线程并发会出现数据覆盖、丢失更新问题。
4.3 典型使用场景
状态标记位:线程启停标记、全局开关;
双重检查锁(DCL)单例模式,禁止指令重排、保证可见性;
简单变量读写,无需复合运算的场景。
五、CAS 无锁机制(乐观锁核心)
5.1 CAS 核心原理
CAS(Compare And Swap,比较并交换),是无锁乐观锁机制,全程无阻塞、无锁开销,高并发性能远超重量级锁。
执行流程:
获取内存中变量的旧预期值;
比较当前内存值与旧值是否一致;
一致则更新为新值,操作成功;不一致则说明被其他线程修改,重试直至成功。
5.2 CAS 优缺点
优点:无锁阻塞、线程无需挂起、CPU利用率高、并发性能优异。
缺点:
ABA问题:变量从A改B再改回A,CAS无法感知数据被修改过;
自旋空转:竞争激烈时,无限重试消耗CPU资源;
只能保证单变量原子性,无法保证多变量复合操作原子性。
5.3 ABA 问题解决方案
使用AtomicStampedReference,通过「变量值+版本号」双重校验,每次修改版本号自增,彻底解决ABA问题。
5.4 JUC原子类实战
/** * CAS原子类实战演示 * AtomicInteger 基于CAS无锁实现线程安全计数 * 无锁阻塞,并发性能优于synchronized */importjava.util.concurrent.atomic.AtomicInteger;publicclassCASDemo{// 初始化原子计数器privatestaticfinalAtomicIntegerCOUNT=newAtomicInteger(0);publicstaticvoidmain(String[]args)throwsInterruptedException{// 10个线程,各累加1000次for(inti=0;i<10;i++){newThread(()->{for(intj=0;j<1000;j++){COUNT.getAndIncrement();}}).start();}Thread.sleep(2000);// 最终结果严格为10000,保证线程安全System.out.println("最终计数结果:"+COUNT.get());}}核心结论:所有JUC原子类底层均基于CAS实现,是JUC并发包的底层基石。
六、AQS 同步队列器(JUC底层核心)
AQS(AbstractQueuedSynchronizer)是Java并发编程顶层底层框架,Lock锁、线程池、CountDownLatch、Semaphore 全部基于AQS实现,是JUC工具类的核心基石。
6.1 AQS 三大核心组件
state同步状态变量:volatile修饰的int变量,代表锁状态,0为无锁、1为加锁、大于1为可重入次数;
双向阻塞队列:竞争锁失败的线程,封装为Node节点进入队列排队,等待唤醒;
独占/共享模式:独占模式(ReentrantLock)、共享模式(读写锁、计数器)。
6.2 AQS 核心执行流程
线程尝试通过CAS修改state状态,抢占锁;
抢占成功:执行业务逻辑;
抢占失败:封装为Node节点,加入双向阻塞队列;
前驱节点执行完毕释放锁,唤醒当前节点线程,再次尝试抢占锁;
循环往复,直至所有线程执行完毕。
6.3 AQS 核心作用(面试满分)
AQS封装了线程排队、阻塞、唤醒、竞争的通用逻辑,开发者只需自定义state状态修改规则,即可快速实现各类同步工具,无需重复编写底层队列与线程调度逻辑,统一了Java并发同步组件的底层实现。
七、线程池万字详解(企业核心+面试压轴)
核心金句:企业开发禁止使用Executors创建线程池,必须手动通过ThreadPoolExecutor自定义,规避资源耗尽风险。
7.1 线程池核心优势
复用线程:避免频繁创建、销毁线程,节省系统开销;
控制并发:限制最大并发线程数,防止线程无限创建导致OOM、CPU飙高;
统一管理:统一调度、监控、执行任务,适配异步、批量、延时场景。
7.2 七大核心参数(必背必考)
corePoolSize 核心线程数:常驻线程,线程池初始化后保留,不会被回收;
maximumPoolSize 最大线程数:线程池允许的最大线程总数,控制并发上限;
keepAliveTime 空闲超时时间:非核心线程空闲超时后自动回收;
unit 时间单位:空闲时间单位;
workQueue 任务阻塞队列:存储等待执行的任务,核心线程满负载后任务入队;
threadFactory 线程工厂:自定义线程创建规则、线程名称、优先级;
handler 拒绝策略:队列满、线程数达上限,触发任务拒绝规则。
7.3 线程池执行流程(满分背诵)
接收新任务,判断当前运行线程数是否小于核心线程数,小于则新建核心线程执行任务;
核心线程已满,判断阻塞队列是否未满,未满则任务入队等待;
队列已满,判断当前线程数是否小于最大线程数,小于则新建非核心线程执行任务;
线程数已达最大值、队列已满,触发拒绝策略。
7.4 四种拒绝策略(面试必问)
AbortPolicy(默认):直接抛出异常,阻止任务执行,便于发现并发过载问题;
CallerRunsPolicy:调用者线程自行执行任务,不丢弃、不报错,降低并发压力;
DiscardPolicy:直接丢弃当前新任务,无异常、无日志,业务慎用;
DiscardOldestPolicy:丢弃队列最旧任务,执行当前新任务,适合时效性优先场景。
7.5 禁止使用Executors的原因(避坑核心)
newFixedThreadPool:无界队列,任务堆积过多引发OOM;
newCachedThreadPool:最大线程数无上限,线程无限创建耗尽CPU与内存;
newSingleThreadExecutor:无界队列,任务堆积OOM风险;
newScheduledThreadPool:线程数无上限,高并发资源耗尽。
企业规范:全部使用ThreadPoolExecutor 手动创建线程池,自定义参数与拒绝策略。
7.6 线程池参数配置规范(实战最优)
CPU密集型任务:核心线程数 = CPU核心数 + 1,减少线程上下文切换;
IO密集型任务:核心线程数 = CPU核心数 * 2,充分利用CPU空闲时间;
业务耗时IO:可适当放大线程数,根据接口吞吐量压测微调。
7.7 标准线程池实战代码(可直接上线)
/** * 企业生产级线程池配置示例 * 禁止使用Executors,手动创建ThreadPoolExecutor * 自定义参数、有界队列、合理拒绝策略,规避OOM与资源耗尽风险 */importjava.util.concurrent.*;publicclassThreadPoolDemo{// 获取当前设备CPU核心数privatestaticfinalintCPU_NUM=Runtime.getRuntime().availableProcessors();// 全局自定义线程池privatestaticfinalThreadPoolExecutorTHREAD_POOL=newThreadPoolExecutor(CPU_NUM+1,// 核心线程数CPU_NUM*2,// 最大线程数10L,// 非核心线程空闲超时时间TimeUnit.SECONDS,// 时间单位newArrayBlockingQueue<>(100),// 有界阻塞队列,限制任务堆积Executors.defaultThreadFactory(),// 默认线程工厂newThreadPoolExecutor.CallerRunsPolicy()// 饱和拒绝策略);publicstaticvoidmain(String[]args){// 批量提交任务for(inti=0;i<20;i++){intindex=i;THREAD_POOL.execute(()->{System.out.println("线程执行任务:"+index+",线程名:"+Thread.currentThread().getName());});}// 优雅关闭线程池THREAD_POOL.shutdown();}}八、JUC并发工具类与并发容器
8.1 三大并发工具类(面试高频)
8.1.1 CountDownLatch(倒计时计数器)
作用:等待多个子线程执行完毕,主线程再执行,适用于批量任务同步等待。
特点:一次性使用,倒计时结束后无法重置。
8.1.2 CyclicBarrier(循环栅栏)
作用:多个线程互相等待,全部到达屏障点后再统一执行,支持循环复用。
特点:可重复使用,适合分段任务、多轮并发计算场景。
8.1.3 Semaphore(信号量)
作用:控制并发线程数量,实现限流、资源抢占,适用于接口限流、连接池控制。
8.2 并发容器核心原理
8.2.1 ConcurrentHashMap(并发Map王者)
核心特性:线程安全、高并发性能优异,替代HashMap与HashTable。
JDK1.8底层原理:数组+链表+红黑树,采用CAS + synchronized分段锁,锁粒度细化到桶节点,并发性能大幅提升。
对比HashTable:HashTable是全局锁,并发串行执行;ConcurrentHashMap是桶级锁,多线程可并行操作不同桶,并发效率极高。
8.2.2 并发队列
ConcurrentLinkedQueue:无锁并发队列,CAS实现,高并发无阻塞;
LinkedBlockingQueue:阻塞队列,可用于线程池、生产者消费者模型。
九、并发高频面试真题(满分背诵版)
9.1 为什么volatile不能保证原子性?
标准答案:volatile仅能保证变量读写的可见性与有序性,无法保证复合操作的原子性。类似i++的操作分为读取、运算、赋值三步,多线程并发时三步操作会出现穿插覆盖,volatile无法锁定整体操作,因此不能保证原子性,原子性需依靠锁或CAS保障。
9.2 AQS的核心原理是什么?
标准答案:AQS是JUC并发组件的底层同步框架,核心基于state同步状态变量 + 双向阻塞队列实现。线程通过CAS竞争state锁状态,竞争失败则封装为节点进入队列阻塞排队,前驱节点释放锁后唤醒后继节点,完成线程调度,所有锁、线程池、并发工具类均基于AQS扩展实现。
9.3 线程池为什么不允许使用Executors?
标准答案:Executors创建的线程池存在严重资源风险:部分线程池无界队列会导致任务无限堆积引发OOM,部分线程池无最大线程限制会导致线程无限创建,耗尽CPU与内存资源。企业开发必须手动使用ThreadPoolExecutor自定义线程池,精准控制队列长度、最大线程数、拒绝策略,规避线上风险。
9.4 synchronized和Lock如何选型?
标准答案:低并发、简单同步场景优先使用synchronized,JDK1.6后锁升级优化性能优异、写法简洁、自动释放锁无死锁风险;高并发、锁竞争激烈、需要公平锁、可中断、超时释放等精细化控制场景,优先使用ReentrantLock,灵活度与并发性能更优。
9.5 CAS的优缺点与ABA解决方案?
标准答案:CAS优点是无锁阻塞、CPU利用率高、并发性能优异;缺点是存在ABA问题、高竞争空转消耗CPU、仅支持单变量原子操作。ABA问题核心解决方案是使用AtomicStampedReference,通过版本号机制记录变量修改次数,精准识别数据篡改,彻底规避ABA隐患。
十、本篇总结
本文全方位拆解Java并发编程全集核心重难点,从零吃透线程、锁、volatile、CAS、AQS、线程池、JUC工具类、并发容器所有知识点,全覆盖开发实战与面试压轴考点:
掌握线程核心概念、生命周期、常用方法区别,彻底理清并发底层基础;
精通synchronized锁升级、ReentrantLock显式锁、读写锁,精准选型各类锁场景;
吃透volatile、CAS无锁机制,理解并发三大特性的解决方案;
掌握AQS底层队列同步原理,读懂所有JUC并发组件底层源码;
熟练使用企业标准线程池,规避OOM、CPU飙高、任务堆积等线上并发Bug;
掌握JUC并发工具类与并发容器,适配高并发业务开发,吃透高频面试真题。
熟练掌握本篇内容,可彻底攻克Java并发难点,具备独立开发高并发业务、排查线上并发问题、应对大厂并发面试压轴提问的能力!
下期预告
下一篇:Java IO与NIO全集|BIO/NIO/AIO区别、缓冲区、通道、多路复用、零拷贝、面试万字详解
深耕Java IO核心体系,从零讲解传统BIO阻塞IO、NIO非阻塞IO、AIO异步IO核心原理,拆解Buffer缓冲区、Channel通道、Selector多路复用器、文件零拷贝机制、Netty底层核心基础,全覆盖IO开发与面试重难点!
持续更新Java零基础全套连载,关注专栏从零学Java,稳步进阶后端开发!