Java 中为什么不推荐在 while 循环中使用 sleep()

2023-11-13 18:00:55 浏览数 (2)

前言

  • 最近逛 CSDN 看到一篇文章,文章大意是说为什么在循环中不推荐使用 sleep 操作,原因在于线程挂起和唤醒会有很大的性能消耗,并推荐使用 Timer 及 ScheduledExecutorService 方案进行优化,下面我简单复现一下具体代码,大家思考一下,原文的原因以及解决方案是否合理:
代码语言:java复制
public class Demo {

    private static final boolean FLAG = true;

    public static void main(String[] args) {
        while (FLAG) {
            try {
                Thread.sleep(3000);
            } catch (InterruptedException e) {
                throw new RuntimeException(e);
            }
        }
        
        // do something
    }
}
  • 上面的代码你可能会得到下面的警告:
代码语言:java复制
Call to ‘Thread.sleep()’ in a loop, probably busy-waiting
// 循环中调用 sleep 可能会导致忙等待 
// 如 FLAG 变量状态未改变 那么线程可能一直循环,并不断进行线程挂起和唤醒
1698754459535.png1698754459535.png

原因是否正确

  • 主要原因和原文博主所说有很大的关系但不完全正确:
  • 我们都知道 Java 线程实际对应着操作系统中的一个线程,对线程的挂起和唤醒是一个很耗性能的操作,因此我们需要避免对线程进行挂起和唤醒;
  • 但还一个重要的原因是忙等待,如上文所示 FLAG 变量的状态可能永远不会被改变,那么线程将会不断进行挂起和唤醒,进入忙等待状态,造成资源的浪费。

方案是否合理

  • 记住一点,讨论方案永远不能脱离场景,没有一种方案可以适应所有的场景,我们永远只是在探讨适合当前场景的方案。因此,原文博主只是提供了一些场景下的方案,下面我们结合一些具体的场景来探讨一些可行的方案:

定时轮询场景

  • 定时轮询的场景我们可以采用博主提供的思路,比如 Timer 及 ScheduledExecutorService 或者其它一些定时机制方案。
代码语言:txt复制
比如微服务体系中,客户端上报实例状态,或者服务端检测客户端状态都会使用定时轮询的机制。

事件机制

  • 上文的场景,我更推荐事件机制进行解耦,当变量被改变时,发送变量修改事件进行处理,如常见的 Spring Event 或者其它事件推送框架。
代码语言:txt复制
比如一些用户登录场景,当用户登录状态改变时,发送登录事件进行后续处理,比如登录通知等等

等待和唤醒

  • 等待和唤醒机制一般适用于等待时间较长的场景,因为等待和唤醒是一个性能消耗比较大的操作;在等待时间不是很长的场景可以使用轮询机制,避免线程频繁的挂起和唤醒。
代码语言:txt复制
在 Java AQS 等待获取锁和线程池任务为空等待新任务时,会使用等待和唤醒操作
轮询机制 和 等待和唤醒 一般会结合使用,避免线程频繁的挂起和唤醒。

个人简介

0 人点赞