CPU 在 Thread.onSpinWait() 期间使用率为 100%

CPU usage is 100% during Thread.onSpinWait()

我正在为我的加密货币交易机器人编写回测原始数据收集器,我 运行 遇到了一个奇怪的优化问题。

我在 Executors.newCachedThreadPool() 运行ning 中经常有 30 个 运行nables 来自 API 的 get 请求。由于 API 的请求限制为每分钟 1200 个,因此我的 运行nable:

中有这段代码
    while (minuteRequests.get() >= 1170) {
        Thread.onSpinWait();
    }

是的,minuteRequests 是一个 AtomicInteger,所以我不会 运行 解决那里的任何问题。

一切正常,问题是即使我使用推荐的忙等待 onSpinWait 方法,当启动等待时,我的使用率从 24% CPU 左右增加到 100%。作为参考,我 运行 在 3900X(24 线程)上安装它。

关于如何更好地处理这种情况有什么建议吗?

我的建议是完全不要忙等待


对于Thread.onSpinWaitjavadocs这样说:

Indicates that the caller is momentarily unable to progress, until the occurrence of one or more actions on the part of other activities. By invoking this method within each iteration of a spin-wait loop construct, the calling thread indicates to the runtime that it is busy-waiting. The runtime may take action to improve the performance of invoking spin-wait loop constructions.

请注意,突出显示的部分使用 may 而不是 will。这意味着它也 可能不会 做任何事情。另外 "improve the performance" 并不意味着您的代码在客观上是高效的。

javadoc 还暗示改进可能取决于硬件。

简而言之,这是正确的使用方法 onSpinwait ...但是您对它的期望太高了。它不会使您的忙等待代码高效。


那么我建议你实际做什么?

我建议您将 AtomicInteger 替换为 Semaphore (javadoc)。此特定循环将替换为以下内容:

semaphore.acquire();

这会阻塞1 直到 1 "permit" 可用并获取它。请参阅 class javadocs 了解信号量如何工作的解释。

注意:由于您没有向我们展示您的速率限制的完整实施,因此不清楚您当前的方法实际如何运作。因此,我无法确切地告诉您如何从头到尾将 AtomicInteger 替换为 Semaphore


1 - 阻塞的线程是 "parked",直到其他线程释放许可。当它被停放时,线程不会 运行 并且不与 CPU 核心相关联。核心要么处于空闲状态(通常处于低功耗状态),要么被分配给其他线程。这通常由操作系统的线程调度程序处理。当另一个线程释放许可时,Semaphore.release 方法将告诉 OS 解停在 acquire.

中阻塞的线程之一