为啥 Thread.sleep 不好用

Posted

技术标签:

【中文标题】为啥 Thread.sleep 不好用【英文标题】:Why Thread.sleep is bad to use为什么 Thread.sleep 不好用 【发布时间】:2013-07-23 11:52:08 【问题描述】:

对于这个重复的问题,我深表歉意,但我还没有找到任何令人满意的答案。大多数问题都有自己的特定用例:Java - alternative to thread.sleepIs there any better or alternative way to skip/avoid using Thread.sleep(1000) in Java?

我的问题是针对非常通用的用例。等待条件完成。做一些操作。检查一个条件。如果条件不成立,等待一段时间再做同样的操作。

例如考虑一种通过调用其 createAPI 表来创建 DynamoDB 表的方法。 DynamoDB 表需要一些时间才能变为活动状态,因此该方法会调用其 DescribeTable API 以定期轮询状态,直到某个时间(假设 5 分钟 - 由于线程调度导致的偏差是可以接受的)。如果表在 5 分钟内变为活动状态,则返回 true,否则抛出异常。

这是伪代码:

public void createDynamoDBTable(String name) 
  //call create table API to initiate table creation

  //wait for table to become active
  long endTime = System.currentTimeMillis() + MAX_WAIT_TIME_FOR_TABLE_CREATE;

  while(System.currentTimeMillis() < endTime) 
    boolean status =  //call DescribeTable API to get status;
    if(status) 
         //status is now true, return
         return
     else 
        try 
            Thread.sleep(10*1000);
         catch(InterruptedException e) 
        
    
  

  throw new RuntimeException("Table still not created");

我了解使用Thread.sleep 会阻塞当前线程,从而消耗资源。但是在一个中等规模的应用程序中,一个线程是一个大问题吗? 我在某处读到使用ScheduledThreadPoolExecutor 并在那里进行状态轮询。但同样,我们必须使用至少 1 个线程来初始化这个池,在该线程中执行轮询的可运行方法将运行。

关于为什么使用Thread.sleep 被认为是个坏主意的任何建议,以及实现上述相同的替代选项是什么。

http://msmvps.com/blogs/peterritchie/archive/2007/04/26/thread-sleep-is-a-sign-of-a-poorly-designed-program.aspx

【问题讨论】:

msmvps.com/blogs/peterritchie/archive/2007/04/26/… 您的链接是关于 .Net 的。它不适用于 Java。 毫无疑问,轮询很糟糕。但如果你没有其他选择,这是你最不应该关心的事情。 @StinePike,这是否应该意味着,答案就在博客中?我已经浏览了这篇文章,但除了资源阻塞(已经提到)和这个The thread needs perform logic every n milliseconds As noted earlier, Sleep means relinquish control. When your thread gets control again isn't up to the thread; so it can't be used for periodic logic.之外,没有看到任何特别的东西,但我认为,他正在谈论恰好在 n 毫秒后进行调度。 投票为“太宽泛”的人请告诉我如何才能使其更具体?我已经问了很多有关方法的用例。 【参考方案1】:

在这种情况下使用Thread.sleep 很好。人们不鼓励Thread.sleep 的原因是因为它经常用于修复竞争条件的不良尝试,用于基于通知的同步是更好的选择等。

在这种情况下,AFAIK 您别无选择,只能轮询,因为 API 不向您提供通知。我还可以看到这是一个不常见的操作,因为大概您不会创建数千个表。

因此,我觉得在这里使用Thread.sleep 很好。正如您所说,无论如何要阻塞当前线程时生成一个单独的线程似乎会使事情复杂化。

【讨论】:

一个重要的警告:确保睡眠线程没有占用稀缺资源,例如互斥锁或数据库连接。 这种模式在以下情况下是否也可以接受? - 一次轮询 SQS 一条消息并维护一个消息列表。当列表大小达到 X 个时,批量处理它们。如果 SQS 中没有更多消息,则休眠一段时间。之后再次投票。 @Jitendra:通常对于 MQ,您希望避免轮询,但 AFAIK 除了在 SQS 中轮询(顺便说一下,即使没有消息也会收费),别无他法,所以Thread.sleep 是一个选项。不过,我可能倾向于使用调度执行器,因为它可以轻松添加消费者、微调轮询调度并强制您分离业务逻辑和基础架构。【参考方案2】:

是的,应该尽量避免使用 Thread.sleep(x) 但不应该完全忘记它:

为什么要避免

它不会释放锁 不保证会在休眠时间后开始执行(因此它可能会一直等待 - 显然是极少数情况) 如果我们错误地将前台处理线程置于睡眠状态,那么我们将无法关闭该应用程序直到 x 毫秒。 我们现在为特定问题(例如设计模式(当然不完全是),为什么要使用 Thread.sleep(x))加载了新的并发包。

在哪里使用 Thread.sleep(x):

用于在后台运行线程中提供延迟 还有一些其他的。

【讨论】:

你能解释一下其他几个吗?顺便说一句,答案很好。

以上是关于为啥 Thread.sleep 不好用的主要内容,如果未能解决你的问题,请参考以下文章

为啥 Thread.Sleep 如此有害

如果 Task.Delay 优于 Thread.Sleep,为啥本书中的示例使用 Thread.Sleep?

为啥 Thread.Sleep 不在秒表中注册?

为啥 thread.sleep 在第一次捕获时不停止?

为啥要放弃使用Thread.Sleep

为啥没有“Thread.sleep”的“while(true)”会在 Linux 上导致 100% 的 CPU 使用率,而在 Windows 上却不会?