0

0

什么是活锁(Livelock)与饥饿现象_线程虽然在运行但无法推进的故障

P粉602998670

P粉602998670

发布时间:2026-02-22 20:21:10

|

480人浏览过

|

来源于php中文网

原创

活锁是线程状态为runnable但业务无进展;饥饿是线程长期处于waiting/timed_waiting却得不到调度;死锁则表现为线程互相等待锁。三者需通过jstack输出的线程状态与堆栈精准区分。

什么是活锁(livelock)与饥饿现象_线程虽然在运行但无法推进的故障

活锁:线程状态是 RUNNABLE,但 CPU 白跑,业务没进展

活锁不是卡死,而是“假忙真停”——线程一直在执行,Thread.getState() 返回 RUNNABLE,CPU 占用不低,但关键逻辑(比如消息处理、任务提交、锁获取)反复失败、原地打转。

典型错误现象:

  • tryLock() 获取锁失败后,立刻 unlock()(其实根本没 lock 成功),再 Thread.sleep(0) 或固定毫秒值重试
  • 分布式任务队列里,失败消息被无条件插回队首,下一轮又第一个被捞出、又失败、又插回
  • 两个线程同时尝试交换资源(如转账 A→B 和 B→A),发现冲突就主动回退并重试,结果永远“你让一步、我让一步”

关键解决点是打破同步节奏:

  • 重试前必须加随机延迟:Thread.sleep(ThreadLocalRandom.current().nextInt(10, 100)),别用 sleep(10) —— 多线程会迅速趋同,冲突复现率反而更高
  • 每次重试都要检查中断:if (Thread.interrupted()) throw new InterruptedException();,否则无法响应 shutdownNow()
  • 避免在重试循环里做非幂等操作(比如发一次通知、写一次日志),否则日志刷屏却无实质推进

饥饿:线程能跑、也想跑,但永远轮不到它

饥饿的线程状态常是 WAITINGTIMED_WAITING,比如卡在 ReentrantLock.lock()Object.wait()、或 LinkedBlockingQueue.take() 上,但不是因为对方死锁,而是调度/排队机制本身不公平。

常见错误场景:

  • 把后台统计线程设成 Thread.MIN_PRIORITY,而前台 HTTP 请求线程占满 CPU,JVM 不保证优先级跨平台生效,该线程可能几小时不调度一次
  • 用默认构造的 ReentrantLock(非公平模式),新线程总比等了 5 秒的老线程更容易抢到锁
  • wait() + notify() 实现生产者消费者,但 notify() 总唤醒刚进来的线程,老等待者一直被跳过

实操建议:

大师兄智慧家政
大师兄智慧家政

58到家打造的AI智能营销工具

下载
  • 禁用 setPriority() —— 它在 Linux/OpenJDK 上基本无效,还引入不可移植风险
  • 需要排队公平性时,显式启用公平锁:new ReentrantLock(true);但注意:吞吐量下降 15–30%,别在高频短临界区滥用
  • 替代 wait()/notify():用 Condition 配合公平锁,或直接上 java.util.concurrent 的公平类,比如 LinkedBlockingQueue(构造时传 true 启用公平模式)

怎么一眼区分活锁、饥饿、死锁?看 jstack 输出+线程状态

别靠猜。出问题第一时间跑 jstack <pid></pid> 或开 JConsole 点“检测死锁”按钮:

  • 如果有死锁,JVM 会直接标出互相等待的线程和锁地址,堆栈里出现 waiting to lock locked 循环对
  • 如果没有死锁,但一堆线程状态是 RUNNABLE,且堆栈反复出现在 tryLock()sleep() → 循环,就是活锁
  • 如果线程停在 parking to wait for java.lang.Object.wait(Native Method),且等待时间远超预期(比如 >30s),大概率是饥饿

注意:WAITING 状态不等于饥饿——它可能是正常阻塞(如 CountDownLatch.await()),得结合业务逻辑和等待时长判断。

最容易被忽略的坑:活锁和饥饿都难复现,但上线后才爆发

本地压测往往看不到活锁,因为线程数少、延迟低、竞争弱;饥饿在单核机器上更隐蔽,因为调度器“假装”给了低优先级线程机会。

真实系统里,这些毛病只在以下情况集中暴露:

  • 流量突增时,重试逻辑被放大,活锁从偶发变常态
  • 后台任务(如定时清理、指标聚合)和前台请求共用线程池,且没做隔离,饥饿线程彻底失联
  • 容器环境(K8s)限制 CPU 时间片,Thread.MIN_PRIORITY 的线程可能被 OS 层直接饿死,比 JVM 层更狠

所以,凡是有重试、有等待、有优先级或锁竞争的地方,都要问一句:它会不会“看起来在跑,其实没动”?

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
DeepSeek
DeepSeek

幻方量化公司旗下的开源大模型平台

豆包大模型
豆包大模型

字节跳动自主研发的一系列大型语言模型

通义千问
通义千问

阿里巴巴推出的全能AI助手

腾讯元宝
腾讯元宝

腾讯混元平台推出的AI助手

文心一言
文心一言

文心一言是百度开发的AI聊天机器人,通过对话可以生成各种形式的内容。

讯飞写作
讯飞写作

基于讯飞星火大模型的AI写作工具,可以快速生成新闻稿件、品宣文案、工作总结、心得体会等各种文文稿

即梦AI
即梦AI

一站式AI创作平台,免费AI图片和视频生成。

ChatGPT
ChatGPT

最最强大的AI聊天机器人程序,ChatGPT不单是聊天机器人,还能进行撰写邮件、视频脚本、文案、翻译、代码等任务。

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

396

2023.08.11

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

247

2023.10.07

if什么意思
if什么意思

if的意思是“如果”的条件。它是一个用于引导条件语句的关键词,用于根据特定条件的真假情况来执行不同的代码块。本专题提供if什么意思的相关文章,供大家免费阅读。

826

2023.08.22

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

421

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

595

2023.08.10

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

421

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

595

2023.08.10

线程和进程的区别
线程和进程的区别

线程和进程的区别:线程是进程的一部分,用于实现并发和并行操作,而线程共享进程的资源,通信更方便快捷,切换开销较小。本专题为大家提供线程和进程区别相关的各种文章、以及下载和课程。

715

2023.08.10

pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法
pixiv网页版官网登录与阅读指南_pixiv官网直达入口与在线访问方法

本专题系统整理pixiv网页版官网入口及登录访问方式,涵盖官网登录页面直达路径、在线阅读入口及快速进入方法说明,帮助用户高效找到pixiv官方网站,实现便捷、安全的网页端浏览与账号登录体验。

1030

2026.02.13

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号