原文来自于:zha-ge.cn/java/88
用错了就翻车!Thread.sleep() vs Thread.yield() 的区别,很多人都踩过坑
其实我当初刚写多线程的时候,真不是很懂 Thread.sleep
和 Thread.yield
。一堆人跟我说"你多线程慢了就 sleep ,想礼让就 yield!",结果一搞项目翻车了。血的教训啊,今天来叨叨下到底这俩有啥门道,别踩我踩过的坑!
有一次领导让我写个限速的小功能,意思是并发快了就歇一会,别把 Redis 打爆,好家伙,我脑子一热就写个 sleep:
java
while (!queue.isEmpty()) {
process(queue.poll());
Thread.sleep(10); // 休息下,别太快......
}
用起来咋感觉还不错?起码我本地压根没问题。可是部署到测试环境一跑,QA直接就问:哥们,你这线程干嘛罢工啊?CPU 一点都没起来,感觉像养老了。
Thread.sleep,真的只是睡觉
说白了,Thread.sleep(mx)
就是真让线程睡觉------你指定多少毫秒,它就"假死"多少毫秒。CPU 直接腾出来了,线程啥事也不管,闹钟响才迷迷糊糊爬起来接着干活。
这也太 literal(字面意思)了吧?但"灵活"真的不是它的长项。你要是 sleep 一长,线程和 CPU 就硬生生等你醒。你要随时被打断,还得自己写:
java
try {
Thread.sleep(10);
} catch (InterruptedException e) {
// 别装死,赶紧收拾跑路啥的
}
再瞧瞧 Thread.yield,那就真是个性子
yield()
就比较像高铁上抢厕所,你发现后面一堆人排队,自己赶紧出来把机会给别人。它对调度器说:"我现在不急,让一下先。" 然而纯"礼让"好像天真了点。调度器接不接受,全凭它心情。有时候你礼让了半天还是你继续上,尴尬不?
比如下面代码,看着优雅,其实有时候毫无卵用:
java
while (workRemain()) {
if (shouldYield()) {
Thread.yield();
}
doWork();
}
但操作系统看心情能不能换人,你 yield 十遍,没准还是你自己,不保证真的"止住"高压力线程。
踩坑瞬间
说个真实故事,我和同事曾经在抢锁环节用 yield()
替代了 sleep()
,图啥好看......理想情况是线程能频繁让出时间片,别死磕。现实中,生产环境直接 CPU 飙红,线程数激增,系统卡爆。为啥?因为 yield()
压根不保证你真让得出去,结果线程忙着自嗨,啥活也没干,全在争调度。
再有一次,反着来,把 sleep(0)
当 yield()
用,想着只让一步,实际是让整个时间片,线程直接处于"睡懒觉"状态,响应慢了好几倍,被 PM 追着问咋回事。
经验启示
我自己薅头发总结了一下,这俩经典用法别整错:
- Thread.sleep(n):
- 想真暂停/限速,一定用它,准确可控,但别懒得捕获 InterruptedException。
- 注意别乱用小数值疯狂 sleep,浪费调度次数,CPU 其实悲伤。
- Thread.yield():
- 只建议在极特殊情况下临时"礼让",比如写协作算法模拟"自旋",不要求马上效果。
- 千万别图省事指望 yield 能帮你防止高 CPU,实话说它调度不"靠谱"。
场景 | 推荐方法 | 备注 |
---|---|---|
限速/节流 | Thread.sleep |
必须暂停就 sleep,别全指望 yield |
协作式算法/自旋 | Thread.yield |
礼让不一定生效,慎用 |
有时候你会觉得多线程很玄乎,工具箱里啥花样都有,真用错一下,集群"嘎"的一下就躺那儿了......所以千万别被名词带得"云里雾里"。
写在最后
反正写线程的事,大家都喜欢糊弄两句,但是真遇上坑,才会理解"语文理解能力"也是代码能力。以后记得:sleep
就是真的睡觉,yield
基本算个"嘴上客气",千万别在关键地方搞混。这种小细节,才是写多线程的下饭小知识! 你有没有被 sleep/yield
坑过?欢迎留言嘲笑一下我,哈哈哈!