文章目录

📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌
📙 作者: 编程技术圈(哇哥面试陪跑)
👉 欢迎关注、分享、评论
✔️ 持续分享更多干货内容
🌐🌏🌎➕tcmeta, 欢迎沟通交流
📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌📌
零、引入
王二的脸涨得像刚蒸好的肉包子,手指在键盘上戳得噼啪响 —— 他写的监控线程占着 CPU 不放,主线程卡得连日志都刷不出来。前几天听人说 Thread.yield () 能 “让线程懂事点”,他抄起就用,结果监控线程是 “让了”,却像放了个空屁,CPU 占用率还是飙到 99%。
“这 yield 到底是啥破烂!说让线程让资源,结果比老赖还顽固!” 王二的吼声惊得窗台上的麻雀扑棱棱飞走。隔壁工位的哇哥正用旧布擦他的搪瓷缸,闻言慢悠悠抬头:“不是 yield 没用,是你把它当救星了 —— 这玩意儿就像车间里的轮岗示意,你举个‘让工位’的牌子,不是让你滚蛋,是让组长重新安排,懂?”
点赞 + 关注,跟着哇哥把 Thread.yield () 的底裤扒干净,3 个真相戳破误区,下次再用这方法,保准比老司机还稳。

一、王二的 “空屁代码”:把 yield 用成 “摆设”

王二写的是系统监控程序,一个线程循环采集数据,一个主线程处理数据。他觉得采集线程太 “霸道”,就加了 yield 想让它 “让着点”,结果全白搭。代码如下,透着股急病乱投医的味儿:
package cn.tcmeta.yield;
import java.util.concurrent.TimeUnit;
/**
* @author: laoren
* @description: 王二的坑:把Thread.yield()当“资源释放器”,纯属误解
* @version: 1.0.0
*/
public class YieldMisuseSample {
// 采集线程:循环采集系统负载
private static class CollectThread extends Thread {
public CollectThread() {
super("Collect-Thread");
}
@Override
public void run() {
long count = 0;
while (!Thread.currentThread().isInterrupted()) {
// 模拟采集数据(空循环,故意占CPU)
count++;
// 王二认为:加yield就能让线程释放CPU
Thread.yield();
// 每采集100万次打印一次
if (count % 1000000 == 0) {
System.out.println(this.getName() + ":已采集" + count / 10000 + "万次");
}
}
}
}
// 主线程:处理采集到的数据
static void main() throws InterruptedException {
CollectThread collectThread = new CollectThread();
collectThread.start();
// 主线程处理数据,看是否被阻塞
long mainCount = 0;
while (true) {
mainCount++;
if (mainCount % 500000 == 0) {
System.out.println("【主线程】:已处理" + mainCount / 10000 + "万次");
}
// 模拟处理耗时
TimeUnit.MICROSECONDS.sleep(1);
}
}
}
运行结果:yield 成了 “空气”,CPU 照样被占满

王二瘫在椅子上,挠着后脑勺:“明明加了 yield,咋采集线程还这么霸道?”
哇哥把擦干净的搪瓷缸往桌上一墩,发出 “当” 的一声脆响:“你这是把 yield 的意思理解反了!yield 不是‘释放 CPU’,是‘提示操作系统:我这活儿暂时不急,你给其他线程分分时间片’—— 但听不听,得看操作系统脸色,更得看线程优先级。”
他指着代码:“你这采集线程和主线程优先级一样,都是 5。yield 喊完,操作系统可能转头又把时间片分给它 —— 就像车间里你举着‘让工位’的牌子,组长看你和下个人都一样重要,说不定还让你接着干。”
二、用 “车间轮岗” 讲透 yield:是 “让工位” 不是 “滚蛋”

哇哥拽过王二桌上的草稿纸,画了个歪歪扭扭的车间,三个工人围着两个工位,旁边标着 “CPU 时间片”—— 这是他的拿手好戏,再玄乎的技术,到他手里都能变成车间里的事儿。
“CPU 就像车间里的两个工位,线程就是工人,” 哇哥指着草稿纸,“每个工人轮流用工位,用完一个时间片(比如 10ms)就下来,这叫‘抢占式调度’。那 yield 是啥?是工人刚用了 5ms,发现手里的活儿是循环检查,不急,就举个牌子说‘组长,我这步不急,让别人先上’—— 这就是 yield 的作用:主动放弃当前时间片的剩余部分,提示操作系统重新调度”
**他顿了顿,用铅笔圈出 “提示” 两个字:“重点是‘提示’,不是‘命令’。**操作系统听不听,看两点:一是有没有优先级更高的线程等着 —— 要是有,立马让;二是优先级相同的线程,看调度算法 —— 可能让,也可能不让。”
🔥 yield 核心真相(王二记在烟盒内侧)
王二掏出皱巴巴的烟盒,用铅笔歪歪扭扭地记,字里行间透着恍然大悟:
- 作用:主动放弃当前 CPU 时间片的剩余部分,将线程状态从 “运行中” 转为 “就绪”,提示操作系统重新调度;
- 不是放弃资源:线程不会阻塞,也不会释放锁,只是回到就绪队列 “排队”;
- 调度依赖 OS:优先级相同的线程可能被重新选中,优先级低的线程几乎没机会;
- 底层原理:调用 Unsafe 类的yield()本地方法,最终由操作系统的线程调度器决定后续调度。
yield 的线程状态变化

三、yield 的 3 个核心真相:戳破 “鸡肋” 谣言

王二还是半信半疑:“照这么说,yield 不就是个‘礼貌性让贤’?那它到底有啥用?”
哇哥冷笑一声,拿出三个代码示例,像甩巴掌似的把王二的误解拍碎:“不是 yield 没用,是你用错了地方。这三个真相,能让你把 yield 用出花来。”
➡️ 真相 1:yield 不释放锁,别指望它解决 “锁霸占”
王二曾以为 yield 能让线程释放锁,结果写了段代码,锁照样被占着。这是他的错误代码:
package cn.tcmeta.yield;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
/**
* @author: laoren
* @description: // 错误认知:认为yield能释放锁
* @version: 1.0.0
*/
public class YieldLockMisuseSample {
private static final Lock LOCK = new ReentrantLock();
private static int count = 0;
static void main() {
// 线程1:持有锁,调用yield
Thread thread1 = new Thread(() -> {
LOCK.lock();
try {
while (count < 10) {
count++;
System.out.println("线程1:count=" + count);
// 以为yield能释放锁,让线程2执行
Thread.yield();
// 模拟耗时操作
TimeUnit.MILLISECONDS.sleep(100);
}
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
} finally {
LOCK.unlock();
}
}, "Thread-1");
// 线程2:等待锁
Thread thread2 = new Thread(() -> {
LOCK.lock();
try {
System.out.println("线程2:终于拿到锁!count=" + count);
} finally {
LOCK.unlock();
}
}, "Thread-2");
thread1.start();
thread2.start();
}
}
运行结果:线程 2 一直等,yield 没释放锁

哇哥指着代码:“看到没?yield 只释放 CPU 时间片,不释放锁 —— 就像工人占着工位还拿着工具,就算举着‘让工位’的牌子,别人也没法用。想释放锁,得用 unlock (),别指望 yield。”
🔥 真相 2:yield 能优化 “忙等”,避免 CPU 空转
这才是 yield 的真正用武之地 —— 当线程在循环中 “忙等” 某个条件(比如等待 flag 变为 true),不用 sleep(睡久了不及时),用 yield 既能让 CPU 给别人用,又能快速响应条件变化。
正确用法:优化忙等场景
package cn.tcmeta.yield;
import java.util.concurrent.TimeUnit;
/**
* @author: laoren
* @description: // yield的正确用法:优化忙等,避免CPU空转
* @version: 1.0.0
*/
public class YieldBusyWaitFix {
// 标记:是否有新数据
private static volatile boolean hasNewData = false;
// 存储数据
private static String data = "";
static void main() {
// 消费者线程:忙等新数据
Thread consumer = new Thread(() -> {
while (!Thread.currentThread().isInterrupted()) {
// 忙等条件:有新数据
while (!hasNewData) {
// 关键:用yield避免CPU空转,主动让时间片
Thread.yield();
}
// 处理数据
System.out.println("消费者:拿到数据=" + data);
// 重置标记
hasNewData = false;
}
}, "Consumer-Thread");
consumer.start();
// 生产者线程:间隔产生数据
Thread producer = new Thread(() -> {
try {
for (int i = 0; i < 5; i++) {
TimeUnit.SECONDS.sleep(2);
data = "新数据-" + i;
hasNewData = true;
System.out.println("生产者:产生数据=" + data);
}
// 中断消费者,结束程序
consumer.interrupt();
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}, "Producer-Thread");
producer.start();
}
}
运行结果:CPU 不空转,响应及时

哇哥解释:“要是不用 yield,消费者线程会在 while (!hasNewData) 里疯狂循环,把 CPU 占满;加了 yield,它每次循环都主动让时间片,CPU 占用率能从 99% 降到 1% 以下 —— 这才是 yield 的真本事。”
✔️ 真相 3:yield 和 sleep 的区别,别再搞混
王二总把 yield 和 sleep 弄混,哇哥干脆画了张表,像私塾先生罚抄似的让他背:

对比代码:yield 和 sleep 的调度差异
package cn.tcmeta.yield;
import java.util.concurrent.TimeUnit;
/**
* @author: laoren
* @description: // yield vs sleep 调度差异
* @version: 1.0.0
*/
public class YieldVsSleepSample {
static void main() {
// 线程1:用yield
Thread yieldThread = new Thread(() -> {
long start = System.currentTimeMillis();
int count = 0;
while (System.currentTimeMillis() - start < 1000) {
count++;
Thread.yield();
}
System.out.println("yield线程:1秒内循环" + count + "次");
}, "Yield-Thread");
// 线程2:用sleep(1ms)
Thread sleepThread = new Thread(() -> {
long start = System.currentTimeMillis();
int count = 0;
while (System.currentTimeMillis() - start < 1000) {
count++;
try {
TimeUnit.MILLISECONDS.sleep(1);
} catch (InterruptedException e) {
Thread.currentThread().interrupt();
}
}
System.out.println("sleep线程:1秒内循环" + count + "次");
}, "Sleep-Thread");
yieldThread.start();
sleepThread.start();
}
}
运行结果:调度差异明显

“看明白了吧?” 哇哥敲着桌子,“yield 是‘让而不退’,还在就绪队列里等着;sleep 是‘睡而不醒’,休眠时根本不在就绪队列 —— 就像工人,yield 是站在工位旁等,sleep 是回休息室躺着,能一样吗?”
四、面试必问:Thread.yield () 核心题(附答案)
哇哥知道王二要面试,特意整理了 3 道高频题,让他抄在小本本上,字里行间都是考点:
✔️ 面试题 1:Thread.yield () 的作用是什么?底层原理是什么?
用 “车间轮岗” 的例子答,面试官一听就懂:
- 作用:线程主动放弃当前 CPU 时间片的剩余部分,从 “运行状态” 转为 “就绪状态”,提示操作系统重新调度 —— 就像工人举牌 “让工位”,但不离开车间;
- 底层原理:调用 Unsafe 类的yield()本地方法(JDK 源码里是Unsafe.yield()),最终由操作系统的线程调度器决定后续调度:
- 若有高优先级线程就绪,优先调度高优先级线程;
- 若无高优先级线程,可能重新调度当前线程(同优先级线程公平竞争)。
🔥 面试题 2:Thread.yield () 和 Thread.sleep () 的核心区别是什么?
抓三个核心点,用 “工人工作” 比喻:
- 线程状态:yield 是 “运行→就绪”,不阻塞,随时可能被重新调度;sleep 是 “运行→阻塞”,休眠期间不会被调度(除非被中断)—— 前者工人站着等,后者工人躺着睡;
- 时间片控制:yield 只释放剩余时间片(比如还剩 5ms,直接让出去);sleep 释放全部时间片,休眠指定时间(比如睡 100ms,到点才醒);
- 适用场景:yield 适合忙等优化(避免 CPU 空转);sleep 适合需要固定间隔的场景(比如每隔 1 秒轮询一次)。
➡️ 面试题 3:Thread.yield () 的使用场景有哪些?要注意什么?
适用场景:
- 忙等优化:线程在循环中等待某个条件(如 volatile 变量变化),用 yield 避免 CPU 空转(比 sleep 响应更及时);
- 平等调度:同优先级线程需要公平竞争 CPU 时,用 yield 避免单个线程长期占用;
- 降低 CPU 占用:非核心线程在执行非紧急任务时,用 yield 减少对核心线程的干扰。
注意事项:
- 不释放锁:yield 不会释放 synchronized 或 Lock 的锁,别用它解决锁竞争问题;
- 调度不保证:yield 是 “提示” 不是 “命令”,操作系统可能忽略,不要依赖它实现线程顺序;
- 低优先级慎用:低优先级线程调用 yield 后,可能长期得不到调度(被高优先级线程抢占)。
五、总结:yield 核心心法(王二编的顺口溜)
王二把核心知识点编成顺口溜,贴在键盘上,字丑但醒目:
- yield 不释放锁,只让时间片就好;
- 运行转就绪,不是阻塞别混淆;
- 忙等循环加 yield,CPU 占用立马少;
- sleep 睡够才醒,yield 随时可能跑;
- 调度看 OS 脸色,别把它当救命草。
王二用优化后的忙等代码替换了之前的监控程序,CPU 占用率从 99% 降到了 5%,领导路过时瞥了眼监控,破天荒地说了句 “这代码写得像回事”。
王二拿着代码跑去找哇哥,脸上笑开了花。哇哥呷了口搪瓷缸里的茶,茶叶梗浮在水面,慢悠悠说出那句收尾的话:
哇哥说:“技术这东西,就像巷子里的老豆腐,看着不起眼的玩意儿,未必没真本事。你之前觉得 yield 是鸡肋,不过是没摸到它的脾气 —— 所谓精通,不是会用多少 API,是能把每个 API 的骨头都啃透,知道它该在哪站岗,不该在哪添乱。这世上本没有没用的技术,只有用错地方的人。”



被折叠的 条评论
为什么被折叠?



