Java 定时任务里写了个循环,每轮 Thread.sleep(1000),跑了半天发现每小时慢了将近一分钟。还有人 sleep 被中断后抛出异常,catch 里什么都没写,任务就这么悄悄停了。

sleep 只保证不小于

参数是毫秒,sleep(1000) 的意思是最少睡一秒,实际可能一秒零几毫秒。操作系统调度、GC、线程唤醒都会往后拖,循环里拿它计时,误差会累积,跑一天差出好几分钟。要按固定节奏跑,就在每轮结束时用 System.nanoTime 算实际耗时再补差,别死等固定值。

精度受系统时钟限制

Windows 默认时钟粒度约 15.6 毫秒,sleep(1) 和 sleep(10) 的真实差别很小。Linux 上一般能到毫秒级,但也别指望微秒。对精度有要求的地方,先测一遍实际耗时,再决定参数。

它不释放锁

在 synchronized 块里 sleep,锁一直握着,别的线程只能在门口排队。要释放锁用 wait,要协作等条件用 Condition。这点搞混,线上表现就是整片接口超时,查半天以为数据库慢。

中断异常不能吞

try { Thread.sleep(1000); } catch (InterruptedException e) { Thread.currentThread().interrupt(); }

catch 之后必须恢复中断标志,否则线程池收不到停止信号。直接打印堆栈或者留个空 catch,是最常见的写法错误,也是排查线程池关不掉的元凶。

定时任务用调度器

用 ScheduledExecutorService 的 scheduleAtFixedRate,它按固定频率触发,不会把执行耗时算进间隔。JDK 19 之后还能写 Thread.sleep(Duration.ofSeconds(1)),可读性好一点,本质没变。sleep(0) 不睡,只提示调度器可以切换,适合放在自旋里降占用。

UI 线程和主线程里别 sleep,安卓上卡住五秒就是 ANR,服务端就是请求堆积。该用调度器的地方,别用睡眠凑。