线程启停
创建
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()用于独立延时任务(如定时轮询),需处理InterruptedExceptionwait()用于多线程协作(如生产者-消费者模型),需处理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与ConditionReentrantLock替代synchronized,Condition提供多条件队列支持,可精确控制唤醒顺序
阻塞队列(
BlockingQueue)线程安全队列自动处理阻塞逻辑(如空队列阻塞消费者,满队列阻塞生产者)
实现类:
ArrayBlockingQueue(固定容量)、LinkedBlockingQueue(可选容量)适用场景:生产者-消费者模式的数据传递与同步
并发工具类
CountDownLatch:主线程等待多个子线程完成任务(如初始化)CyclicBarrier:多线程相互等待至共同点后继续(可重用)Semaphore:控制资源并发访问数(如连接池)
管道流(
PipedInputStream/PipedOutputStream)基于字节流的单向数据传输,适用简单数据传递场景,性能较低
信号量与原子类
Semaphore控制资源访问权限,支持公平/非公平策略AtomicInteger等原子类实现无锁线程安全计数或状态管理
评论区