线程启停
创建
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等原子类实现无锁线程安全计数或状态管理
 
 
             
           
             
                         
             
            
评论区