一、前言
 
多线程是Java后端开发的核心重难点,也是面试高频考点、高并发业务的底层基石。在业务开发中,文件批量处理、接口异步调用、定时任务、高并发秒杀、日志异步打印等场景,都离不开多线程技术。
 
很多开发者初学多线程时,只会简单创建线程,却不懂线程状态流转、线程安全底层原理、锁的优劣、并发工具适用场景,最终导致线上出现数据错乱、死锁、CPU飙高、程序卡死等疑难问题。
 
本文将从零开始,循序渐进讲解Java多线程全套知识:从基础概念、线程创建方式、线程生命周期,到核心线程安全机制、锁体系、线程通信、高级并发工具,最后结合实战场景与避坑技巧,帮你彻底吃透Java多线程,适配日常开发与面试需求。
 
二、核心基础概念:进程与线程
 
2.1 进程
 
进程是操作系统资源分配的最小单位,是独立运行的程序。
 
每个进程拥有独立的内存空间、CPU资源、文件句柄等系统资源,进程之间相互隔离、互不干扰。例如:IDEA、浏览器、微信,每一个运行的软件都是一个独立进程。
 
2.2 线程
 
线程是操作系统调度CPU的最小单位,是进程内的执行单元。
 
一个进程包含至少一个主线程,多个线程共享进程的内存资源(堆内存、方法区),同时拥有自己独立的栈内存。线程切换成本远低于进程,因此多线程是实现并发高效执行的核心方式。
 
2.3 进程与线程核心区别
 
1. 资源归属:进程独立占有资源,线程共享进程资源;
2. 隔离性:进程完全隔离,线程共享数据、存在安全风险;
3. 开销成本:进程创建、销毁、切换开销极大,线程开销极小;
4. 通信方式:进程间通信复杂(管道、socket),线程间可直接共享变量通信;
5. 容错性:单个进程崩溃不影响其他进程,单个线程异常可能导致整个进程终止。
 
2.4 并发与并行
 
- 并发:单核CPU,通过时间片轮转,多个线程交替执行,宏观同时、微观交替;
- 并行:多核CPU,多个线程同一时刻真正同时执行。
 
Java多线程的核心价值:最大化利用CPU资源,提升程序执行效率,实现异步任务处理。
 
三、Java线程的5种创建方式
 
Java中线程本质是 Thread 类,所有创建方式最终都依赖 Thread 底层实现,日常开发常用5种创建方式,各有适用场景。
 
3.1 方式一:继承Thread类
 
自定义类继承 Thread ,重写 run() 方法(线程执行体),调用 start() 启动线程。
 
 
 
缺点:Java单继承机制,继承Thread类后无法再继承其他类,扩展性差。
 
3.2 方式二:实现Runnable接口
 
实现 Runnable 接口,重写 run() 方法,将任务传入Thread对象启动,推荐使用。
 
 
 
优点:避免单继承限制,任务与线程解耦,支持多线程共享同一个任务。
 
3.3 方式三:匿名内部类创建
 
简化代码,无需定义独立类,适合临时简单任务。
 
 
 
3.4 方式四:Lambda表达式创建(JDK8+)
 
基于函数式接口简化写法,代码最简洁,日常开发首选。
 
 
 
3.5 方式五:实现Callable + FutureTask(带返回值)
 
前面四种方式无返回值、无法抛出异常, Callable 是JDK5新增接口,支持线程任务返回结果、异常捕获,适合需要获取任务执行结果的场景。
 
 
 
重要注意事项
 
1. 启动线程必须调用 start() ,禁止直接调用run():直接调用run()只是普通方法执行,不会创建新线程;
2. 一个线程只能调用一次start(),重复调用会抛出 IllegalThreadStateException 异常。
 
四、Java线程七大生命周期
 
很多开发问题(线程卡死、任务不执行),根源是不懂线程状态流转。
 
Java线程在JDK1.5后定义七大状态,可通过 Thread.getState() 获取:
 
状态 含义 场景 
NEW(新建) 线程对象创建,未启动 new Thread()之后、start()之前 
RUNNABLE(就绪/运行) 可执行状态,等待CPU调度 调用start()后,包含就绪、运行两种子状态 
BLOCKED(阻塞) 等待锁资源 未获取到synchronized锁,阻塞等待 
WAITING(无限等待) 无限期等待,需主动唤醒 调用wait()、join()、LockSupport.park() 
TIMED_WAITING(超时等待) 限时等待,超时自动唤醒 调用sleep(long)、wait(long)、join(long) 
BLOCKED/WAITING 阻塞等待资源/信号 被动暂停执行 
TERMINATED(终止) 线程执行完毕/异常终止 run()执行结束、线程异常退出 
 
核心状态流转流程
 
 NEW  → 调用 start()  →  RUNNABLE  → 获取CPU资源执行 → 遇到锁/等待方法 →  BLOCKED/WAITING/TIMED_WAITING  → 资源就绪/超时唤醒 →  RUNNABLE  → 任务执行完毕 →  TERMINATED 
 
五、线程安全问题与解决方案
 
5.1 什么是线程安全问题?
 
多线程同时操作共享资源(共享变量),由于CPU切换、指令重排、可见性问题,导致最终数据结果错乱。
 
不安全代码演示(经典售票案例)
 
 
 
问题现象:出现重复售票、超卖(票数为负数) ,典型线程安全问题。
 
5.2 三大线程安全问题根源
 
1. 原子性:多条操作指令被CPU中断、拆分执行;
2. 可见性:多线程缓存变量,主内存数据更新对其他线程不可见;
3. 有序性:JVM、CPU为优化性能进行指令重排,打乱代码执行顺序。
 
5.3 线程安全四大解决方案
 
1. synchronized 同步锁(内置锁)
 
Java原生关键字,独占、可重入锁,解决原子性+可见性+有序性问题,分为两种用法:
 
- 同步代码块(推荐,粒度更细,性能更高)
 
 
 
- 同步方法(锁当前对象/类,粒度粗)
 
 
 
缺点:悲观锁、阻塞式等待、无法中断、不支持超时、读写互斥。
 
2. Lock 显式锁(JDK5+)
 
 java.util.concurrent.locks.Lock 接口,手动加锁、手动释放锁,功能比synchronized更强大。
 
核心实现类: ReentrantLock (可重入独占锁)
 
 
 
ReentrantLock 优势:
 
- 可中断等待、可超时等待;
- 支持公平锁(线程排队获取锁);
- 可绑定多个条件,精准唤醒线程。
 
3. volatile 关键字
 
轻量级同步机制,仅解决可见性、有序性,不保证原子性,适合状态标记变量。
 
核心场景:多线程读写状态标识(开关变量)
 
 
 
4. 原子类(JUC)
 
 java.util.concurrent.atomic 包下工具类,无锁CAS机制,保证数值运算原子性,性能远超锁。
 
常用类: AtomicInteger 、 AtomicLong 、 AtomicBoolean 
 
 
 
六、线程通信机制
 
多线程并非独立执行,业务中需要线程间协作(等待、唤醒、交替执行),核心通信方式3种:
 
6.1 wait / notify / notifyAll(Object原生方法)
 
-  wait() :当前线程释放锁,进入无限等待;
-  notify() :随机唤醒一个等待线程;
-  notifyAll() :唤醒所有等待线程。
 
经典案例:生产者消费者模式
 
 
 
6.2 Condition 精准唤醒(Lock配套)
 
替代 wait/notify ,可以精准指定唤醒某一组线程,避免无效唤醒,性能更高。
 
 
 
6.3 join() 线程插队
 
让当前线程阻塞等待目标线程执行完毕,再继续执行自身任务,适合线程顺序控制。
 
 
 
七、JUC高级并发工具类(开发必备)
 
JDK5提供 java.util.concurrent 并发工具包,解决复杂多线程场景,4大核心工具:
 
7.1 CountDownLatch 倒计时计数器
 
作用:让主线程等待多个子线程全部执行完毕后再执行,一次性使用。
 
场景:批量任务处理、接口多线程汇总结果
 
 
 
7.2 CyclicBarrier 循环栅栏
 
作用:多个线程到达屏障点后同时执行,可循环复用。
 
场景:多线程分批同步执行、多人组队任务
 
 
 
7.3 Semaphore 信号量
 
作用:控制最大并发线程数,实现限流、资源抢占。
 
场景:接口限流、数据库连接池、资源配额控制
 
 
 
7.4 CompletableFuture 异步任务(JDK8+)
 
作用:简化异步编程,支持任务链式调用、结果组合、异常处理,替代传统Callable+Future。
 
场景:接口异步调用、多任务并行汇总
 
 
 
八、死锁原理与解决方案
 
8.1 什么是死锁?
 
多个线程互相持有对方需要的锁资源,同时互相等待,无限僵持,程序永久阻塞。
 
8.2 死锁产生四大必要条件(缺一不可)
 
1. 互斥条件:锁资源独占,同一时间仅一个线程持有;
2. 请求保持:线程持有旧锁,同时请求新锁;
3. 不可剥夺:锁只能主动释放,不能被其他线程抢占;
4. 循环等待:线程间形成锁资源循环等待链。
 
8.3 死锁代码演示
 
 
 
8.4 死锁解决方案
 
核心思路:破坏四大必要条件任意一个
 
1. 统一锁顺序:所有线程按照固定顺序获取锁(最常用);
2. 超时等待:使用Lock的tryLock(time),超时放弃锁请求;
3. 避免嵌套锁:尽量减少锁嵌套使用;
4. 资源分级:对锁资源排序,杜绝循环等待。
 
九、线程池:生产环境必用技术
 
9.1 为什么要用线程池?
 
频繁创建、销毁线程开销极大,线程池可以:
 
1. 复用线程,降低资源消耗;
2. 统一管理线程,控制并发数;
3. 避免无限创建线程导致OOM;
4. 支持任务排队、拒绝策略、定时执行。
 
9.2 线程池核心参数
 
 
 
9.3 生产规范
 
禁止使用Executors创建线程池(可能导致OOM、并发溢出),必须手动自定义ThreadPoolExecutor,根据业务场景配置核心参数。
 
十、多线程高频避坑总结
 
1. 禁止直接调用run()启动线程,必须用start();
2. 共享变量必须做线程安全处理,局部变量天然线程安全;
3. volatile不保证原子性,数值累加场景禁用;
4. Lock锁必须在finally中释放,避免死锁;
5. 杜绝锁嵌套、无序锁请求,防止死锁;
6. 高并发场景优先使用线程池+原子类,减少synchronized使用;
7. 线程任务必须捕获异常,避免单线程异常导致整体任务中断;
8. 定时任务、异步业务优先使用CompletableFuture、线程池,禁止手动创建大量线程。
 
十一、总结
 
Java多线程的学习可分为三层体系:
 
1. 基础层:进程线程概念、线程创建、生命周期、线程调度;
2. 核心层:线程安全三大特性、锁机制(synchronized/Lock/volatile)、线程通信;
3. 进阶层:JUC并发工具、线程池、死锁排查、高并发实战优化。
 
多线程是高并发编程的基础,懂原理、会实战、能避坑,才能解决线上复杂并发问题,也是进阶高级开发工程师的必备能力。后续可以基于本文知识点,深入学习JUC底层CAS、AQS原理、线程池源码、并发容器等高阶内容。

更多推荐