Thread.sleep让兔子“睡过头”是因为它粗暴暂停线程且不释放锁,若写在循环开头则每轮固定休眠,无法模拟随机懒惰;应改用Random生成波动休眠时间,并避免在synchronized块中调用。

龟兔赛跑里 Thread.sleep 为什么总让兔子“睡过头”?
因为 Thread.sleep 不释放锁,也不受线程调度优先级影响,它只是粗暴地暂停当前线程指定毫秒数——而兔子逻辑里如果把 Thread.sleep(1000) 写在循环开头,就真的一觉睡满 1 秒,不管乌龟跑没跑完。
常见错误现象:Thread.sleep 被放在临界区外却误以为能“模拟懒惰”,结果兔子线程休眠期间乌龟线程反复抢占 CPU,兔子一醒发现比赛早结束了。
- 真正该睡的是“兔子每跑几步后随机停顿”,不是固定每轮都睡
- 用
Random.nextLong(500, 2000)生成波动休眠时间,比写死Thread.sleep(1000)更贴近设定 - 别在
synchronized块里调Thread.sleep——它不释放锁,会卡住乌龟的进度更新
乌龟和兔子谁先到终点?用 volatile 还是 AtomicBoolean 控制终止?
用 volatile boolean finished 简单但危险:多个线程同时写 finished = true 没问题,但“检查是否到达终点 + 更新 UI + 停止线程”这三步不是原子的,容易出现乌龟冲线后兔子又跑了一步才停。
更稳妥的做法是用 AtomicBoolean 配合 compareAndSet(false, true) 做一次性的终点宣告:
立即学习“Java免费学习笔记(深入)”;
你接手一个需求时,是不是经常这样:"改一个小字段 → 结果把不相关的代码也改了"、"需求说得很模糊 → 做着做着发现跑偏了"、"数据库加个字段 → 上线才发现漏了数据兼容"、"做了个新功能 → 一个月后自己都忘了当时怎么设计的"、"写到一半被中断 → 再回来不知道从哪继续";这个 Skill 就是专门解决这些问题的。 它会自动探测你的项目环境(JDK、Spring Boot、ORM、数据库、前端框架),记在
if (position >= FINISH_LINE && !raceOver.compareAndSet(false, true)) {
return; // 已有人夺冠,直接退出
}
-
volatile适合只读广播(比如显示当前领先者),不适合做“首次抵达”的判定 -
AtomicBoolean的compareAndSet是 JVM 层级的 CAS,能保证只有一个线程成功标记结束 - 别用
Thread.interrupt()强制停止——兔子休眠中被中断会抛InterruptedException,处理不好反而让线程提前退出
怎么让乌龟稳扎稳打、兔子忽快忽慢?用 wait()/notifyAll() 协作还是各自跑各自的?
不需要 wait()/notifyAll()。龟兔赛跑本质是**竞态关系,不是生产者-消费者**。加锁协作只会拖慢整体节奏,还容易引发死锁——比如兔子刚进 synchronized 块就睡了,乌龟在门外干等。
正确思路是:共享状态只读 + 终止信号单向通知 + 进度更新无锁化。
- 终点坐标
FINISH_LINE设为static final int,不改就不需同步 - 每个线程用自己的
position变量,只在更新全局显示时读取对方值(读操作无需加锁) - UI 刷新用单独线程或定时器拉取两个
position值,避免在工作线程里调 Swing/AWT 方法
为什么 System.out.println 一多,乌龟就变快了?
因为 System.out.println 是同步方法,内部有 synchronized (this)。当兔子频繁打印日志,它实际在和乌龟线程争抢 PrintStream 锁,导致兔子执行变慢——这不是你写的逻辑变慢,是 I/O 阻塞把你拖累了。
- 开发阶段可保留打印,但正式运行前务必删掉或重定向到异步 logger(如 SLF4J + Logback 的 async appender)
- 测试性能时,把所有
System.out.println替换成if (DEBUG) System.out.println(...),再用编译期常量控制开关 - 别用
System.nanoTime()测单次Thread.sleep精度——JVM 和 OS 调度本身就有 10–15ms 误差,测出来不准还误导判断
真正的难点不在怎么让线程动起来,而在于怎么让它们“看起来像在比赛”:进度要可感知、终点要唯一、干扰要可控。随便加个 sleep 或锁,往往最先破坏的就是这个观感。


















