"/>
侧边栏壁纸
博主头像
PySuper 博主等级

千里之行,始于足下

  • 累计撰写 286 篇文章
  • 累计创建 17 个标签
  • 累计收到 2 条评论

目 录CONTENT

文章目录

Java 多线程

PySuper
2022-11-06 / 0 评论 / 0 点赞 / 8 阅读 / 0 字
温馨提示:
所有牛逼的人都有一段苦逼的岁月。 但是你只要像SB一样去坚持,终将牛逼!!! ✊✊✊

线程启停

创建

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() 方法启动线程

停止

  1. 标志位控制退出

    • Java 使用 volatile 布尔变量作为退出标志,线程循环检查标志并主动退出

    • Python 通过 threading.Event 或全局变量实现,支持安全退出和资源清理

  2. 中断机制(Java 推荐)

    • 调用 Thread.interrupt() 设置中断标志,通过 isInterrupted() 检查状态

    • 阻塞方法(如 sleep())触发 InterruptedException,需捕获并恢复中断标志

  3. 线程池管理工具

    • Java 的 ExecutorService 通过 shutdown() 平缓关闭或 shutdownNow() 强制终止

    • 使用 Future.cancel(true) 取消正在执行的任务

  4. Python 特有方法

    • 守护线程随主线程结束自动终止,适合无需清理的后台任务

    • concurrent.futures 模块支持通过 ThreadPoolExecutor 取消任务

  5. 强制终止风险

    • Java 的 Thread.stop() 已废弃,可能导致资源泄漏或数据不一致

    • Python 的 _stop() 非官方 API,存在安全隐患

  6. 开发实践建议

    • Java 优先选择中断机制或标志位,Python 推荐 threading.Event

    • 处理阻塞操作时需恢复中断状态(如 try-catch 内调用 interrupt()

    • 高并发场景使用线程池统一管理,避免直接操作线程

异常

调用 interrupt 是如何让线程抛出异常的?

  1. 中断标志位触发机制

    • interrupt() 设置线程中断标志位(falsetrue),不会直接中断运行中的线程

    • 阻塞方法(如 sleep()wait())内部自动检查标志位,若为 true 则抛出 InterruptedException

  2. 阻塞状态下的异常流程

    • 线程在 sleep() 等阻塞状态被中断时,JVM 会

      • 清除中断标志位(重置为 false

      • 抛出 InterruptedException 使线程退出阻塞

  3. 非阻塞状态行为差异

    • 线程运行时调用 interrupt() 仅设置标志位,需通过 isInterrupted() 主动检查并终止任务

    • LockSupport.park() 等操作被中断时不抛异常,但保持标志位为 true,需手动检查

  4. 中断标志位动态处理

    • 抛出 InterruptedException 的方法会重置标志位,捕获异常后需调用 interrupt() 重新标记中断状态

  5. 底层实现原理

    • JVM 在阻塞方法中隐式插入中断检查逻辑,检测到标志位为 true 时触发本地方法抛异常,依赖线程调度器协作

线程状态

  1. NEW 新建状态

    • 线程对象通过 new Thread() 创建后未调用 start(),未关联操作系统底层线程

  2. RUNNABLE 可运行状态

    • 调用 start() 后进入此状态,包含就绪(等待 CPU 分配)和运行中(执行 run() 方法)两种子状态

    • 操作系统层面的阻塞 I/O 在 Java 中仍归为 RUNNABLE

  3. BLOCKED 阻塞状态

    • 竞争 synchronized 锁失败时进入此状态,进入监视器锁的阻塞队列,等待锁释放后被唤醒

  4. WAITING 无限等待状态

    • 主动调用 Object.wait()Thread.join()LockSupport.park(),需其他线程显式唤醒(如 notify()interrupt()

  5. TIMED_WAITING 超时等待状态

    • 调用带超时参数的方法如 Thread.sleep(long)Object.wait(long),超时或中断后自动恢复 RUNNABLE

  6. TERMINATED 终止状态

    • run() 方法执行完毕或抛出未捕获异常后进入此状态,不可重启

  7. 状态转换规则

    • NEW → RUNNABLE:调用 start()

    • RUNNABLE → BLOCKED:竞争 synchronized 锁失败

    • RUNNABLE → WAITING:调用 wait()/join()

    • RUNNABLE → TIMED_WAITING:调用 sleep()/wait(long)

    • BLOCKED → RUNNABLE:成功获取锁

    • WAITING/TIMED_WAITING → RUNNABLE:被唤醒、中断或超时

blocked & waiting

  1. 触发条件与锁资源行为

    • BLOCKED:被动触发(锁竞争失败),不释放已持有的锁

    • WAITING:主动调用 wait()join()park(),释放当前对象锁

  2. 唤醒机制

    • BLOCKED:锁被释放后自动唤醒,参与锁竞争

    • WAITING:需显式唤醒(如 notify()unpark() 或目标线程终止)

  3. 应用场景

    • BLOCKED:高并发锁竞争(如多线程访问共享资源)

    • WAITING:线程协作(如生产者-消费者模型)

  4. 状态转换

    • BLOCKED → RUNNABLE:成功获取锁后转换

    • WAITING → RUNNABLE:唤醒后需重新获取锁,成功则进入 RUNNABLE,失败则转为 BLOCKED

  5. JVM 管理队列

    • BLOCKED:同步队列(Entry Set)

    • WAITING:等待队列(Wait Set)

总结

特性

BLOCKED

WAITING

触发方式

被动(锁竞争失败)

主动(调用等待方法)

锁释放

不释放锁(若未持有则不涉及)

必须释放当前对象锁

唤醒机制

自动(锁可用时)

依赖外部显式操作(如 notify()

常见场景

高并发锁竞争

线程协作等待条件

JVM 管理队列

同步队列(Entry Set)

等待队列(Wait Set)

wait 状态下的线程如何进行恢复到 running 状态?

  1. 显式唤醒

    • 其他线程在同步块内调用 notify()notifyAll(),唤醒一个或所有等待线程

    • 被唤醒线程转为 BLOCKED 状态尝试重新获取锁,成功则进入 RUNNABLE

  2. 中断唤醒

    • 外部调用 interrupt() 使等待线程抛出 InterruptedException,需在 catch 块处理异常并调用 Thread.currentThread().interrupt() 恢复中断标志

  3. 锁竞争与状态转换

    • 被唤醒线程必须重新竞争锁

      • 成功获取锁 → RUNNABLE

      • 锁被占用 → BLOCKED 等待锁释放

  4. 注意事项

    • wait()/notify() 必须在同步块内调用,否则抛出 IllegalMonitorStateException

    • 推荐用 while 循环检查条件,防止虚假唤醒(未被 notify 但被唤醒)

    • LockSupport.park() 的线程需通过 unpark() 唤醒,无锁要求但需管理许可(permit)

wait/notify

notify & notifyAll

  1. 唤醒线程数量

    • notify() 随机唤醒一个等待线程

    • notifyAll() 唤醒所有等待线程

  2. 适用场景

    • notify() 适合单一任务场景(如生产者仅需唤醒一个消费者)

    • notifyAll() 适合条件变化需全体响应的场景(如资源释放后通知所有消费者)

  3. 性能与风险

    • notify() 性能开销小,但可能导致线程饥饿或条件未满足的线程被唤醒

    • notifyAll() 安全性高,但会增加锁竞争和上下文切换开销

  4. 锁竞争机制

    • notify() 唤醒的线程直接进入锁池竞争锁

    • notifyAll() 唤醒的所有线程需先进入锁池竞争,只有获取锁的线程继续执行

  5. 编码要求

    • 两者必须在 synchronized 块内调用,否则抛出 IllegalMonitorStateException

    • 被唤醒线程需用 while 循环检查条件,避免虚假唤醒

    • 若无特殊需求,优先使用 notifyAll() 保证安全性

notify 选择线程

  1. JVM 规范与实现差异

    • 官方规范:notify() 唤醒的线程是任意的(arbitrary),具体选择由 JVM 实现决定

    • HotSpot 实现:按等待队列的 FIFO 顺序唤醒第一个线程(如线程 A → B → C 等待时,先唤醒 A)

  2. 实际唤醒机制

    • 等待队列结构:wait() 进入队列的线程按调用顺序排列,notify() 从队列头部选取线程唤醒

    • 被唤醒线程需重新竞争锁,可能因锁被占用再次进入 BLOCKED 状态

  3. 开发者应对策略

    • 避免依赖唤醒顺序:不同 JVM 实现可能不同,代码设计需假设唤醒顺序不可控

    • 使用 while 循环检查条件:确保被唤醒后验证条件,避免虚假唤醒或非目标线程执行

    • 明确唤醒目标:通过条件变量或多监视器对象间接控制唤醒逻辑,而非依赖 notify() 默认行为

  4. notifyAll() 对比

    • notifyAll() 唤醒所有线程,但最终仅一个线程获取锁,适用于条件变化需全体响应的场景

系统线程

  1. 本质关系:1:1 映射实现

    • 现代 Java 线程(JDK 1.2+)基于操作系统原生线程实现(如 Linux 的 pthread),每个 Java 线程对应一个内核线程

    • 通过 pthread_create 等系统调用创建内核线程,由操作系统调度器(如 Linux CFS)分配 CPU 时间片

    • 例如 new Thread() 会触发 JVM 调用 pthread_create

  2. 核心区别:抽象层级与资源管理

    • 调度机制

      • Java 线程优先级(如 Thread.MAX_PRIORITY)会被映射到操作系统优先级(如 Linux 的 nice 值),映射规则因平台而异

      • 操作系统可通过 sched_setscheduler() 设置实时调度策略(如 SCHED_FIFO),需 root 权限且影响系统稳定性

    • 状态管理

      • Java 线程状态(RUNNABLEBLOCKED)是 JVM 逻辑状态,不完全对应内核线程的 readywaiting 状态

    • 资源开销:Java 线程需额外 JVM 堆内存,上下文切换成本高于原生线程

  3. 历史演变与新型线程

    • 绿色线程(JDK 1.2 前):用户态线程,JVM 自行调度,无法利用多核

    • 虚拟线程(Java 19+):轻量级用户态线程,由 JVM 调度,多虚拟线程复用少量内核线程,适合高并发 I/O 场景

  4. 开发注意事项

    • 线程数量控制:避免过度创建内核线程(受限于 ulimit -u),推荐使用 ThreadPoolExecutor

    • 线程类型选择

      • CPU 密集型任务:优先传统线程(1:1 模型)

      • I/O 密集型任务:优先虚拟线程(减少上下文切换)

    • 跨平台差异:Windows 支持 7 级优先级,Linux 优先级范围更窄,需谨慎依赖优先级逻辑

多线程

  1. 线程安全问题

    • 共享资源竞争:多线程同时修改共享变量(如计数器)会导致数据不一致

      • 解决方案

        • 使用 synchronizedLock 或原子类(如 AtomicInteger

        • 或通过线程安全容器(如 ConcurrentHashMap)管理共享数据

    • 不可变对象:优先设计不可变对象(如 String),避免同步开销

  2. 死锁与活锁

    • 死锁条件:互斥、持有并等待、不可抢占、循环等待

      • 预防:按固定顺序获取锁,使用 tryLock() 超时机制,避免嵌套锁

    • 活锁:线程不断重试失败操作(如消息处理冲突),可通过随机退避策略解决

  3. 同步机制的选择

    • synchronized:简单但粒度粗,适用于低竞争场景

    • Lock/Condition:更灵活(如可中断、超时),适合高竞争或复杂条件等待

    • CAS 无锁编程:通过 Atomic 类实现,适合高并发但低冲突场景(如计数器)

  4. 资源管理与泄漏

    • 线程泄漏:未正确关闭线程池,导致线程持续占用资

      • 建议:使用 ExecutorService 并调用 shutdown(),避免手动创建线程

    • 资源未释放:锁或 I/O 资源未在 finally 块中释放,导致死锁或泄漏

  5. 性能与开销

    • 上下文切换成本:线程过多会导致 CPU 时间浪费在切换而非任务执行

      • 优化:根据任务类型(CPU/I/O 密集)调整线程池大小,或使用协程(如虚拟线程)

    • 伪共享(False Sharing):多个线程修改同一缓存行的不同变量,导致缓存失效

      • 解决:使用 @Contended 注解或填充字节对齐变量

  6. 内存可见性

    • volatile 关键字:确保变量修改对其他线程立即可见,但无法保证原子性

    • happens-before 规则:通过同步操作(如 synchronizedLock)建立内存屏障,避免指令重排序问题

  7. 异常处理

    • 未捕获异常:线程内未捕获的异常会导致线程终止且无日志

      • 处理:通过 UncaughtExceptionHandler 统一捕获并记录异常

  8. ThreadLocal 的使用与风险

    • 适用场景:保存线程私有数据(如数据库连接、用户会话)

    • 内存泄漏:线程池中线程复用可能导致 ThreadLocal 数据长期未被清理

      • 解决:使用后调用 remove() 方法清除数据

  9. 并发工具的正确使用

    • 并发容器:优先使用 ConcurrentHashMapCopyOnWriteArrayList 代替同步容器(如 Hashtable

    • CountDownLatch/CyclicBarrier:协调多线程阶段性任务,避免忙等待

  10. 平台与版本差异

    • 虚拟线程(Java 19+):适用于高并发 I/O 场景,但需注意与传统线程的交互

    • 操作系统限制:Linux 默认线程数限制(ulimit -u),需调整参数支持大规模线程池

数据一致性

  1. 事务型协议方案

    • 两阶段提交 (2PC):协调者与参与者分准备、提交两阶段交互,保证强一致性,但存在单点故障和性能瓶颈

    • 三阶段提交 (3PC):增加预提交阶段,减少阻塞时间,复杂度更高

    • TCC 模式:通过 Try-Confirm-Cancel 实现柔性事务,需手动补偿逻辑,适合复杂业务

  2. 异步最终一致性方案

    • 消息队列:通过 Kafka、RabbitMQ 异步传递数据变更事件,结合重试机制实现最终一致性

    • Saga 模式:拆分长事务为子事务,失败时触发补偿操作,适合订单、支付等链式场景

  3. 锁与校验机制

    • 分布式锁:使用 Redis/Zookeeper 实现互斥访问,注意锁超时和死锁问题

    • 数据版本控制:基于时间戳或序列号实现乐观锁(CAS),减少锁竞争

    • 哈希校验:计算数据哈希值,用于快速验证一致性,适用于文件传输

  4. 数据同步策略

    • 强一致性复制:采用 Paxos、Raft 协议确保多副本实时同步,适合金融等高要求场景

    • 异步复制:通过日志同步降低延迟,容忍短期不一致,适合跨地域系统

    • 双写策略:主备数据库同时写入,结合冲突检测(如 Last Write Wins)保障一致性

  5. 新型技术方案

    • 区块链:基于共识算法(如 PoW/PoS)实现不可篡改账本,适合供应链、版权管理

    • 云原生数据库:如 CockroachDB 支持多活架构和自动分片,内置强一致性

    • 数据网格 (Data Mesh):通过领域自治和联邦治理实现跨平台一致性,需标准化接口

  6. 实践建议

    • 强一致性选 2PC/TCC,最终一致性优先消息队列或 Saga

    • 高并发场景避免锁竞争,采用无锁设计或异步补偿

    • 结合监控告警和自动修复机制,降低不一致风险

sleep & wait

  1. 所属类与方法类型

    • sleep()Thread 类的静态方法,可直接通过类名调用

    • wait()Object 类的实例方法,必须在同步代码块或方法中调用

  2. 锁的释放行为

    • sleep() 不释放持有的锁,其他线程无法进入同步代码块

    • wait() 会释放当前对象的锁,允许其他线程获取锁执行同步代码

  3. 唤醒机制与状态

    • sleep() 超时后自动恢复,线程进入 TIMED_WAITING 状态

    • wait() 需通过 notify()/notifyAll() 或超时唤醒

      • 无超时参数时进入 WAITING 状态

      • 有超时参数时进入 TIMED_WAITING 状态

  4. 使用场景与异常处理

    • sleep() 用于独立延时任务(如定时轮询),需处理 InterruptedException

    • wait() 用于多线程协作(如生产者-消费者模型),需处理 InterruptedExceptionIllegalMonitorStateException(未正确同步时抛出)

  5. 参数与底层实现

    • sleep() 支持纳秒级精度参数(实际依赖操作系统调度)

    • wait() 必须通过对象锁调用,支持超时参数(毫秒 + 纳秒)

  6. 总结

特性

sleep()

wait()

锁释放

❌ 不释放

✅ 释放

调用位置

任意位置

同步块/方法内

唤醒方式

自动恢复

notify() 或超时

线程状态

TIMED_WAITING

WAITING / TIMED_WAITING

适用场景

独立延时任务

多线程协作

sleep 会释放 cpu 吗

  1. CPU 释放机制

    • Thread.sleep() 调用时线程主动让出 CPU 时间片,进入 TIMED_WAITING 状态

    • 操作系统将其移出可运行队列,CPU 资源分配给其他就绪线程

  2. 锁资源的保持

    • sleep() 不会释放已持有的锁(如 synchronized 同步块中的锁),其他线程无法获取同一锁

  3. 性能影响与上下文切换

    • 短时间频繁调用 sleep()(如毫秒级)会导致高频线程切换,增加 CPU 调度开销

    • 建议:

      • 高并发场景优先延长休眠时间(如 10ms 替代 1ms)

      • 或使用异步等待(如 CompletableFuture.delay()

  4. 使用场景建议

    • 适用场景:独立延时任务(如定时轮询)、模拟耗时操作、调试

    • 不适用场景:

      • 线程协作(需用 wait()/notify()

      • 高精度实时任务(受操作系统调度影响)

  5. 异常处理

    • 必须捕获 InterruptedException,并在捕获后通过 Thread.currentThread().interrupt() 恢复中断状态

线程通信

  1. 等待/通知机制(wait()/notify()

    • 基于共享对象的监视器锁实现:线程调用 wait() 释放锁并等待,其他线程通过 notify()/notifyAll() 唤醒

    • 适用场景:生产者-消费者模型等需要协调顺序的共享资源操作

    • 注意:必须在 synchronized 代码块内使用,配合 while 循环防止虚假唤醒

  2. 共享变量与 volatile

    • 通过共享变量传递数据,volatile 保证可见性但不保证原子性

    • 适用场景:简单状态标志(如线程启停控制)

  3. LockCondition

    • ReentrantLock 替代 synchronizedCondition 提供多条件队列支持,可精确控制唤醒顺序

  4. 阻塞队列(BlockingQueue

    • 线程安全队列自动处理阻塞逻辑(如空队列阻塞消费者,满队列阻塞生产者)

    • 实现类:ArrayBlockingQueue(固定容量)、LinkedBlockingQueue(可选容量)

    • 适用场景:生产者-消费者模式的数据传递与同步

  5. 并发工具类

    • CountDownLatch:主线程等待多个子线程完成任务(如初始化)

    • CyclicBarrier:多线程相互等待至共同点后继续(可重用)

    • Semaphore:控制资源并发访问数(如连接池)

  6. 管道流(PipedInputStream/PipedOutputStream

    • 基于字节流的单向数据传输,适用简单数据传递场景,性能较低

  7. 信号量与原子类

    • Semaphore 控制资源访问权限,支持公平/非公平策略

    • AtomicInteger 等原子类实现无锁线程安全计数或状态管理


0
  1. 支付宝打赏

    qrcode alipay
  2. 微信打赏

    qrcode weixin

评论区