线程启停
创建
5种方式
继承 Thread 类
通过继承并重写 run() 方法创建线程,适合简单任务。缺点:单继承限制灵活性(如 Java)
实现接口(Runnable/Callable)
Runnable:解耦任务与线程管理,支持多线程共享同一任务对象
Callable:支持返回值和异常处理,需配合 FutureTask 使用,适合异步结果场景
C 语言的 pthread:使用 pthread_create 函数创建线程,需链接 pthread 库
函数/任务传递(无需继承)
直接传递函数或 Lambda 表达式(如 Python 的 target 参数、Java 的 Lambda)
线程池管理
Java 的 ExecutorService:复用线程资源,控制线程数量和执行策略
Python 的 ThreadPoolExecutor:自动管理线程生命周期,适合高并发场景
其他高级方式
协程:单线程内并发(如 Python asyncio),适合 I/O 密集型任务
守护线程:主线程结束时自动终止,适用于后台任务(如日志监控)
第三方库:如 Java 的 Akka(基于 Actor 模型)提供细粒度并发控制
选择
简单任务优先函数传递
需返回值用 Callable
高并发用线程池
跨平台/C 开发用 pthread
启动
通过 Thread.start() 方法启动线程
停止
标志位控制退出
Java 使用
volatile
布尔变量作为退出标志,线程循环检查标志并主动退出Python 通过
threading.Event
或全局变量实现,支持安全退出和资源清理
中断机制(Java 推荐)
调用
Thread.interrupt()
设置中断标志,通过isInterrupted()
检查状态阻塞方法(如
sleep()
)触发InterruptedException
,需捕获并恢复中断标志
线程池管理工具
Java 的
ExecutorService
通过shutdown()
平缓关闭或shutdownNow()
强制终止使用
Future.cancel(true)
取消正在执行的任务
Python 特有方法
守护线程随主线程结束自动终止,适合无需清理的后台任务
concurrent.futures
模块支持通过ThreadPoolExecutor
取消任务
强制终止风险
Java 的
Thread.stop()
已废弃,可能导致资源泄漏或数据不一致Python 的
_stop()
非官方 API,存在安全隐患
开发实践建议
Java 优先选择中断机制或标志位,Python 推荐
threading.Event
处理阻塞操作时需恢复中断状态(如
try-catch
内调用interrupt()
)高并发场景使用线程池统一管理,避免直接操作线程
异常
调用 interrupt 是如何让线程抛出异常的?
中断标志位触发机制
interrupt()
设置线程中断标志位(false
→true
),不会直接中断运行中的线程阻塞方法(如
sleep()
、wait()
)内部自动检查标志位,若为true
则抛出InterruptedException
阻塞状态下的异常流程
线程在
sleep()
等阻塞状态被中断时,JVM 会清除中断标志位(重置为
false
)抛出
InterruptedException
使线程退出阻塞
非阻塞状态行为差异
线程运行时调用
interrupt()
仅设置标志位,需通过isInterrupted()
主动检查并终止任务LockSupport.park()
等操作被中断时不抛异常,但保持标志位为true
,需手动检查
中断标志位动态处理
抛出
InterruptedException
的方法会重置标志位,捕获异常后需调用interrupt()
重新标记中断状态
底层实现原理
JVM 在阻塞方法中隐式插入中断检查逻辑,检测到标志位为
true
时触发本地方法抛异常,依赖线程调度器协作
线程状态
NEW 新建状态
线程对象通过
new Thread()
创建后未调用start()
,未关联操作系统底层线程
RUNNABLE 可运行状态
调用
start()
后进入此状态,包含就绪(等待 CPU 分配)和运行中(执行run()
方法)两种子状态操作系统层面的阻塞 I/O 在 Java 中仍归为 RUNNABLE
BLOCKED 阻塞状态
竞争
synchronized
锁失败时进入此状态,进入监视器锁的阻塞队列,等待锁释放后被唤醒
WAITING 无限等待状态
主动调用
Object.wait()
、Thread.join()
或LockSupport.park()
,需其他线程显式唤醒(如notify()
、interrupt()
)
TIMED_WAITING 超时等待状态
调用带超时参数的方法如
Thread.sleep(long)
、Object.wait(long)
,超时或中断后自动恢复 RUNNABLE
TERMINATED 终止状态
run()
方法执行完毕或抛出未捕获异常后进入此状态,不可重启
状态转换规则
NEW → RUNNABLE:调用
start()
RUNNABLE → BLOCKED:竞争
synchronized
锁失败RUNNABLE → WAITING:调用
wait()
/join()
RUNNABLE → TIMED_WAITING:调用
sleep()
/wait(long)
BLOCKED → RUNNABLE:成功获取锁
WAITING/TIMED_WAITING → RUNNABLE:被唤醒、中断或超时
blocked & waiting
触发条件与锁资源行为
BLOCKED:被动触发(锁竞争失败),不释放已持有的锁
WAITING:主动调用
wait()
、join()
或park()
,释放当前对象锁
唤醒机制
BLOCKED:锁被释放后自动唤醒,参与锁竞争
WAITING:需显式唤醒(如
notify()
、unpark()
或目标线程终止)
应用场景
BLOCKED:高并发锁竞争(如多线程访问共享资源)
WAITING:线程协作(如生产者-消费者模型)
状态转换
BLOCKED → RUNNABLE:成功获取锁后转换
WAITING → RUNNABLE:唤醒后需重新获取锁,成功则进入 RUNNABLE,失败则转为 BLOCKED
JVM 管理队列
BLOCKED:同步队列(Entry Set)
WAITING:等待队列(Wait Set)
总结
wait 状态下的线程如何进行恢复到 running 状态?
显式唤醒
其他线程在同步块内调用
notify()
或notifyAll()
,唤醒一个或所有等待线程被唤醒线程转为 BLOCKED 状态尝试重新获取锁,成功则进入 RUNNABLE
中断唤醒
外部调用
interrupt()
使等待线程抛出InterruptedException
,需在catch
块处理异常并调用Thread.currentThread().interrupt()
恢复中断标志
锁竞争与状态转换
被唤醒线程必须重新竞争锁
成功获取锁 → RUNNABLE
锁被占用 → BLOCKED 等待锁释放
注意事项
wait()
/notify()
必须在同步块内调用,否则抛出IllegalMonitorStateException
推荐用
while
循环检查条件,防止虚假唤醒(未被notify
但被唤醒)LockSupport.park()
的线程需通过unpark()
唤醒,无锁要求但需管理许可(permit)
wait/notify
notify & notifyAll
唤醒线程数量
notify()
随机唤醒一个等待线程notifyAll()
唤醒所有等待线程
适用场景
notify()
适合单一任务场景(如生产者仅需唤醒一个消费者)notifyAll()
适合条件变化需全体响应的场景(如资源释放后通知所有消费者)
性能与风险
notify()
性能开销小,但可能导致线程饥饿或条件未满足的线程被唤醒notifyAll()
安全性高,但会增加锁竞争和上下文切换开销
锁竞争机制
notify()
唤醒的线程直接进入锁池竞争锁notifyAll()
唤醒的所有线程需先进入锁池竞争,只有获取锁的线程继续执行
编码要求
两者必须在
synchronized
块内调用,否则抛出IllegalMonitorStateException
被唤醒线程需用
while
循环检查条件,避免虚假唤醒若无特殊需求,优先使用
notifyAll()
保证安全性
notify 选择线程
JVM 规范与实现差异
官方规范:
notify()
唤醒的线程是任意的(arbitrary),具体选择由 JVM 实现决定HotSpot 实现:按等待队列的 FIFO 顺序唤醒第一个线程(如线程 A → B → C 等待时,先唤醒 A)
实际唤醒机制
等待队列结构:
wait()
进入队列的线程按调用顺序排列,notify()
从队列头部选取线程唤醒被唤醒线程需重新竞争锁,可能因锁被占用再次进入 BLOCKED 状态
开发者应对策略
避免依赖唤醒顺序:不同 JVM 实现可能不同,代码设计需假设唤醒顺序不可控
使用
while
循环检查条件:确保被唤醒后验证条件,避免虚假唤醒或非目标线程执行明确唤醒目标:通过条件变量或多监视器对象间接控制唤醒逻辑,而非依赖
notify()
默认行为
与
notifyAll()
对比notifyAll()
唤醒所有线程,但最终仅一个线程获取锁,适用于条件变化需全体响应的场景
系统线程
本质关系:1:1 映射实现
现代 Java 线程(JDK 1.2+)基于操作系统原生线程实现(如 Linux 的
pthread
),每个 Java 线程对应一个内核线程通过
pthread_create
等系统调用创建内核线程,由操作系统调度器(如 Linux CFS)分配 CPU 时间片例如
new Thread()
会触发 JVM 调用pthread_create
核心区别:抽象层级与资源管理
调度机制:
Java 线程优先级(如
Thread.MAX_PRIORITY
)会被映射到操作系统优先级(如 Linux 的nice
值),映射规则因平台而异操作系统可通过
sched_setscheduler()
设置实时调度策略(如SCHED_FIFO
),需 root 权限且影响系统稳定性
状态管理:
Java 线程状态(
RUNNABLE
、BLOCKED
)是 JVM 逻辑状态,不完全对应内核线程的ready
或waiting
状态
资源开销:Java 线程需额外 JVM 堆内存,上下文切换成本高于原生线程
历史演变与新型线程
绿色线程(JDK 1.2 前):用户态线程,JVM 自行调度,无法利用多核
虚拟线程(Java 19+):轻量级用户态线程,由 JVM 调度,多虚拟线程复用少量内核线程,适合高并发 I/O 场景
开发注意事项
线程数量控制:避免过度创建内核线程(受限于
ulimit -u
),推荐使用ThreadPoolExecutor
线程类型选择:
CPU 密集型任务:优先传统线程(1:1 模型)
I/O 密集型任务:优先虚拟线程(减少上下文切换)
跨平台差异:Windows 支持 7 级优先级,Linux 优先级范围更窄,需谨慎依赖优先级逻辑
多线程
线程安全问题
共享资源竞争:多线程同时修改共享变量(如计数器)会导致数据不一致
解决方案:
使用
synchronized
、Lock
或原子类(如AtomicInteger
)或通过线程安全容器(如
ConcurrentHashMap
)管理共享数据
不可变对象:优先设计不可变对象(如
String
),避免同步开销
死锁与活锁
死锁条件:互斥、持有并等待、不可抢占、循环等待
预防:按固定顺序获取锁,使用
tryLock()
超时机制,避免嵌套锁
活锁:线程不断重试失败操作(如消息处理冲突),可通过随机退避策略解决
同步机制的选择
synchronized:简单但粒度粗,适用于低竞争场景
Lock/Condition:更灵活(如可中断、超时),适合高竞争或复杂条件等待
CAS 无锁编程:通过
Atomic
类实现,适合高并发但低冲突场景(如计数器)
资源管理与泄漏
线程泄漏:未正确关闭线程池,导致线程持续占用资
建议:使用
ExecutorService
并调用shutdown()
,避免手动创建线程
资源未释放:锁或 I/O 资源未在
finally
块中释放,导致死锁或泄漏
性能与开销
上下文切换成本:线程过多会导致 CPU 时间浪费在切换而非任务执行
优化:根据任务类型(CPU/I/O 密集)调整线程池大小,或使用协程(如虚拟线程)
伪共享(False Sharing):多个线程修改同一缓存行的不同变量,导致缓存失效
解决:使用
@Contended
注解或填充字节对齐变量
内存可见性
volatile 关键字:确保变量修改对其他线程立即可见,但无法保证原子性
happens-before 规则:通过同步操作(如
synchronized
、Lock
)建立内存屏障,避免指令重排序问题
异常处理
未捕获异常:线程内未捕获的异常会导致线程终止且无日志
处理:通过
UncaughtExceptionHandler
统一捕获并记录异常
ThreadLocal 的使用与风险
适用场景:保存线程私有数据(如数据库连接、用户会话)
内存泄漏:线程池中线程复用可能导致
ThreadLocal
数据长期未被清理解决:使用后调用
remove()
方法清除数据
并发工具的正确使用
并发容器:优先使用
ConcurrentHashMap
、CopyOnWriteArrayList
代替同步容器(如Hashtable
)CountDownLatch/CyclicBarrier:协调多线程阶段性任务,避免忙等待
平台与版本差异
虚拟线程(Java 19+):适用于高并发 I/O 场景,但需注意与传统线程的交互
操作系统限制:Linux 默认线程数限制(
ulimit -u
),需调整参数支持大规模线程池
数据一致性
事务型协议方案
两阶段提交 (2PC):协调者与参与者分准备、提交两阶段交互,保证强一致性,但存在单点故障和性能瓶颈
三阶段提交 (3PC):增加预提交阶段,减少阻塞时间,复杂度更高
TCC 模式:通过 Try-Confirm-Cancel 实现柔性事务,需手动补偿逻辑,适合复杂业务
异步最终一致性方案
消息队列:通过 Kafka、RabbitMQ 异步传递数据变更事件,结合重试机制实现最终一致性
Saga 模式:拆分长事务为子事务,失败时触发补偿操作,适合订单、支付等链式场景
锁与校验机制
分布式锁:使用 Redis/Zookeeper 实现互斥访问,注意锁超时和死锁问题
数据版本控制:基于时间戳或序列号实现乐观锁(CAS),减少锁竞争
哈希校验:计算数据哈希值,用于快速验证一致性,适用于文件传输
数据同步策略
强一致性复制:采用 Paxos、Raft 协议确保多副本实时同步,适合金融等高要求场景
异步复制:通过日志同步降低延迟,容忍短期不一致,适合跨地域系统
双写策略:主备数据库同时写入,结合冲突检测(如 Last Write Wins)保障一致性
新型技术方案
区块链:基于共识算法(如 PoW/PoS)实现不可篡改账本,适合供应链、版权管理
云原生数据库:如 CockroachDB 支持多活架构和自动分片,内置强一致性
数据网格 (Data Mesh):通过领域自治和联邦治理实现跨平台一致性,需标准化接口
实践建议
强一致性选 2PC/TCC,最终一致性优先消息队列或 Saga
高并发场景避免锁竞争,采用无锁设计或异步补偿
结合监控告警和自动修复机制,降低不一致风险
sleep & wait
所属类与方法类型
sleep()
是Thread
类的静态方法,可直接通过类名调用wait()
是Object
类的实例方法,必须在同步代码块或方法中调用
锁的释放行为
sleep()
不释放持有的锁,其他线程无法进入同步代码块wait()
会释放当前对象的锁,允许其他线程获取锁执行同步代码
唤醒机制与状态
sleep()
超时后自动恢复,线程进入 TIMED_WAITING 状态wait()
需通过notify()
/notifyAll()
或超时唤醒无超时参数时进入 WAITING 状态
有超时参数时进入 TIMED_WAITING 状态
使用场景与异常处理
sleep()
用于独立延时任务(如定时轮询),需处理InterruptedException
wait()
用于多线程协作(如生产者-消费者模型),需处理InterruptedException
和IllegalMonitorStateException
(未正确同步时抛出)
参数与底层实现
sleep()
支持纳秒级精度参数(实际依赖操作系统调度)wait()
必须通过对象锁调用,支持超时参数(毫秒 + 纳秒)
总结
sleep 会释放 cpu 吗
CPU 释放机制
Thread.sleep()
调用时线程主动让出 CPU 时间片,进入 TIMED_WAITING 状态操作系统将其移出可运行队列,CPU 资源分配给其他就绪线程
锁资源的保持
sleep()
不会释放已持有的锁(如synchronized
同步块中的锁),其他线程无法获取同一锁
性能影响与上下文切换
短时间频繁调用
sleep()
(如毫秒级)会导致高频线程切换,增加 CPU 调度开销建议:
高并发场景优先延长休眠时间(如 10ms 替代 1ms)
或使用异步等待(如
CompletableFuture.delay()
)
使用场景建议
适用场景:独立延时任务(如定时轮询)、模拟耗时操作、调试
不适用场景:
线程协作(需用
wait()
/notify()
)高精度实时任务(受操作系统调度影响)
异常处理
必须捕获
InterruptedException
,并在捕获后通过Thread.currentThread().interrupt()
恢复中断状态
线程通信
等待/通知机制(
wait()
/notify()
)基于共享对象的监视器锁实现:线程调用
wait()
释放锁并等待,其他线程通过notify()
/notifyAll()
唤醒适用场景:生产者-消费者模型等需要协调顺序的共享资源操作
注意:必须在
synchronized
代码块内使用,配合while
循环防止虚假唤醒
共享变量与
volatile
通过共享变量传递数据,
volatile
保证可见性但不保证原子性适用场景:简单状态标志(如线程启停控制)
Lock
与Condition
ReentrantLock
替代synchronized
,Condition
提供多条件队列支持,可精确控制唤醒顺序
阻塞队列(
BlockingQueue
)线程安全队列自动处理阻塞逻辑(如空队列阻塞消费者,满队列阻塞生产者)
实现类:
ArrayBlockingQueue
(固定容量)、LinkedBlockingQueue
(可选容量)适用场景:生产者-消费者模式的数据传递与同步
并发工具类
CountDownLatch
:主线程等待多个子线程完成任务(如初始化)CyclicBarrier
:多线程相互等待至共同点后继续(可重用)Semaphore
:控制资源并发访问数(如连接池)
管道流(
PipedInputStream
/PipedOutputStream
)基于字节流的单向数据传输,适用简单数据传递场景,性能较低
信号量与原子类
Semaphore
控制资源访问权限,支持公平/非公平策略AtomicInteger
等原子类实现无锁线程安全计数或状态管理
评论区