前言
线程是并发编程的最小执行单元,也是Java高并发体系的基石。无论是锁机制、AQS、线程池、异步任务、中间件底层,全部建立在线程调度的基础之上。
本文基于核心底层原理**,**讲解Java线程核心知识点,覆盖基础原理、面试重难点
一、线程本质:栈结构与操作系统调度模型
1.1 线程的底层结构
线程是操作系统调度CPU的最小单位(进程是资源分配最小单位)。
每一个Java线程,在JVM内部都对应一块独立的虚拟机栈(栈帧):
-
方法调用 → 栈帧入栈
-
方法执行结束/返回 → 栈帧出栈
线程之间栈内存相互隔离、互不干扰 ,这也是:局部变量天然线程安全,成员变量/静态变量线程不安全的根本原因。
1.2 多线程诞生的核心意义
CPU 的运算速度远远快于磁盘、网络、IO 读写速度。
单线程最大问题:IO阻塞导致CPU空转浪费。
当线程执行IO操作(网络请求、文件读写、数据库查询)时,线程会阻塞、让出CPU,此时CPU无事可做,只能空等。
多线程的核心价值:填补IO空窗期,榨干CPU性能。
在线程A IO阻塞期间,CPU可以调度线程B、C执行计算任务,极大提升整体吞吐量。
1.3 多线程一定更快吗?
不一定!多线程只优化「IO密集型任务」
-
IO密集型 :网络、文件、数据库,阻塞时间长 → 多线程大幅提速
-
CPU密集型 :纯计算、循环、算法,几乎无阻塞 → 多线程不提速,甚至变慢
原因:线程创建、销毁、上下文切换需要消耗大量CPU资源。如果任务本身没有大量空闲阻塞,多线程只会徒增切换开销。
终极结论(工程选型准则)
-
任务阻塞占比高 → 多线程提升效率
-
任务阻塞占比低(纯计算) → 单线程效率最高
二、线程优先级
2.1 线程优先级本质:代码级别无效
Java提供1~10级线程优先级,但仅为JVM提示,操作系统不保证遵守。
真实生产环境:不要依赖线程优先级控制执行顺序,完全不可靠。
2.2 面试高频:如何实现多线程公平竞争CPU?
原生线程启动时机不同、OS随机调度,无法公平竞争。
工程经典方案:notStart 就绪等待 + notEnd 统一终止
-
notStart:所有线程先就绪、阻塞等待,全部就位后统一放开竞争,保证起跑线一致
-
notEnd:统一控制所有线程停止,避免线程执行参差不齐
实现代码:

三、Java线程七大状态
Java线程并非简单的运行/停止,JDK定义七大线程状态,全部由 Thread.State 枚举维护:
-
NEW 新建:new Thread(),未启动
-
RUNNABLE 就绪:调用start(),等待CPU调度
-
RUNNING 运行:被OS选中,正在执行代码
-
BLOCKED 阻塞:抢锁失败,等待synchronized锁
-
WAITING 无限等待:wait()、join(),无限等待唤醒
-
TIMED_WAITING 限时等待:sleep(time)、wait(time)
-
TERMINATED 死亡:线程执行完毕/异常终止

四、高频面试对比:sleep VS wait
这是并发面试最高频基础题,必须精准区分:
1. 锁行为不同(最核心)
-
sleep :只让出CPU,不释放锁,其他线程无法获取锁
-
wait :让出CPU + 强制释放锁,其他线程可以竞争锁执行
2. 所属类不同
-
sleep:Thread类静态方法
-
wait:Object类方法
3. 使用限制不同
-
wait:必须在同步代码块/锁内执行,否则直接抛异常
-
sleep:任意位置可执行
4. 唤醒机制不同
-
sleep:时间到自动唤醒
-
wait:必须被notify/notifyAll手动唤醒
五、Object类多线程核心方法汇总
Object是所有类父类,其方法支撑了Java最原始的并发协作模型:
1. 并发专属方法
-
wait():释放锁、无限等待
-
wait(time):限时等待
-
notify():随机唤醒一个等待线程
-
notifyAll():唤醒全部等待线程
2. 通用核心方法
-
toString()、equals()、hashCode():对象判等与打印
-
getClass():获取运行时类对象
-
clone():创建对象副本(浅克隆)
六、守护线程 Daemon(后台线程)
6.1 守护线程定义
Java线程分为两类:
-
前台线程(用户线程):业务主线程,JVM必须等待其执行完毕
-
守护线程(后台线程) :为前台线程服务,所有前台线程结束,守护线程直接终止
典型案例:GC垃圾回收线程,全程后台运行,程序退出则GC直接退出。
6.2 应用场景
心跳检测、日志异步刷盘、监控统计、后台巡检,不阻塞程序退出的任务。
七、线程中断机制
7.1 核心真相:Java没有真正的强制中断
Java无法强行杀死正在运行的线程!
interrupt() 仅仅是 修改线程内部的中断标志位,发一个"中断通知"。
线程是否退出、何时退出,由线程自身代码判断、自主决定。
7.2 两种中断模型
1. 原生中断标记中断(interrupt)
线程阻塞状态(sleep/wait)下调用interrupt,会直接抛出中断异常,唤醒线程。
正常运行线程,只会修改标记位,不会停止。
2. 自定义变量中断(工程最常用)
定义全局布尔变量作为开关,外部修改开关,线程内部循环判断,安全退出。
面试结论:线程终止只有两种方式:
-
run() 方法正常执行完毕
-
run() 内部抛出未捕获异常
实现代码:

八、经典面试题:使用join实现多线程严格顺序输出
通过链式join,让线程1执行完再执行线程2,线程2执行完再执行线程3,完美实现顺序串行执行。
在下面代码里面的Thread.join()使用逻辑:当前执行代码的线程阻塞等待,直到前置线程执行完毕join方法才返回(让调用线程主动等待目标线程死亡,实现线程执行顺序控制)
实现代码:

九、ThreadLocal
9.1 核心作用
实现线程数据隔离,多线程互不冲突。
ThreadLocal 可以理解为:每个线程专属的私有存储空间。
9.2 底层原理
每个线程内部维护一个 ThreadLocalMap,以 ThreadLocal 为 key、存储数据为 value。
-
线程只能读取自己Map中的数据
-
无法访问其他线程的数据
9.3 弱引用机制(面试必问)
ThreadLocal的key为弱引用 ,目的:防止内存泄漏。
当ThreadLocal对象无外部引用时,可被GC回收,避免线程常驻导致的内存溢出。
十、线程池核心原理
10.1 为什么必须用线程池?
频繁创建、销毁线程开销极大(操作系统级资源消耗)。
线程池核心价值:
-
线程复用,避免频繁创建销毁
-
统一管理线程、统一分配任务
-
控制最大并发数,防止系统OOM、CPU打满
-
缓冲任务,削峰填谷,提升系统稳定性
10.2 线程池核心思想
线程固定、任务排队,解决"任务产生速度 > 线程执行速度"的并发矛盾。