-2 java concurrency performance multithreading wait
我正在为我的加密交易机器人编写一个回测原始数据收集器,但我遇到了一个奇怪的优化问题。
我在 Executors.newCachedThreadPool() 中经常有 30 个 runnables 运行从 API 获取请求。由于 API 的请求限制为每分钟 1200 次,因此我的可运行文件中有以下代码:
while (minuteRequests.get() >= 1170) {
Thread.onSpinWait();
}
Run Code Online (Sandbox Code Playgroud)
是的,minuteRequests 是一个 AtomicInteger,所以我没有遇到任何问题。
一切正常,问题是即使我使用推荐的忙等待 onSpinWait 方法,当等待开始时,我的 CPU 使用率从 24% 左右上升到 100%。作为参考,我在 3900X(24 线程)上运行它。
关于如何更好地处理这种情况的任何建议?
我的建议是不要做忙等待可言。
的的javadoc为Thread.onSpinWait
这样说:
表示调用者暂时无法继续,直到其他活动的一个或多个动作发生。通过在自旋等待循环构造的每次迭代中调用此方法,调用线程向运行时指示它正在忙等待。运行时可能会采取措施来提高调用自旋等待循环构造的性能。
请注意突出显示的部分使用单词may而不是will。这意味着它也可能什么都不做。此外,“提高性能”并不意味着您的代码在客观上是高效的。
javadoc 还暗示改进可能取决于硬件。
简而言之,这是正确的使用方式onSpinwait
……但是您对它期望过高。它不会使您的忙等待代码高效。
那么我会建议你实际做什么?
我建议您将其替换AtomicInteger
为Semaphore
( javadoc )。此特定循环将替换为以下内容:
semaphore.acquire();
Run Code Online (Sandbox Code Playgroud)
这会阻塞1,直到 1 个“许可”可用并获得它。有关信号量如何工作的说明,请参阅类 javadocs。
注意:由于您没有向我们展示您的速率限制的完整实现,目前尚不清楚您当前的方法实际上是如何工作的。因此,我无法确切地告诉您如何替换AtomicInteger
with Semaphore
。
1 - 被阻塞的线程被“停放”,直到其他线程释放许可。当它停放时,线程不会运行并且不与 CPU 内核相关联。内核要么处于空闲状态(通常处于低功耗状态),要么被分配给其他线程。这通常由操作系统的线程调度程序处理。当另一个线程释放许可时,该Semaphore.release
方法将告诉操作系统解除在中阻塞的线程之一acquire
。