工作窃取是现代线程池用来减少工作队列争用的一种技术。
经典的线程池有一个队列,每个线程池线程锁定队列,使任务出队,然后解锁队列。如果任务很短而且数量很多,那么队列上就会出现很多争用。在这里使用无锁队列确实有帮助,但并不能完全解决问题。
现代线程池使用工作窃取——每个线程都有自己的队列。当一个线程池线程产生一个任务时——它将它排入自己的队列。当一个线程池线程想要让一个任务出队时——它首先尝试将一个任务从他自己的队列中出队,如果没有的话——它会从其他线程队列中“窃取”工作。这确实减少了线程池的争用并提高了性能。
newWorkStealingPool
创建一个使用工作窃取的线程池,其中线程数作为处理器数。
newWorkStealingPool
提出了一个新问题。如果我有四个逻辑核心,那么池将总共有四个线程。如果我的任务阻塞 - 例如在同步 IO 上 - 我没有充分利用我的 CPU。我想要的是在任何给定时刻有四个活动线程,例如 - 加密 AES 的四个线程和等待 IO 完成的另外 140 个线程。
这就是ForkJoinPool
提供 - 如果您的任务产生新任务并且该任务等待它们完成 - 池将注入新的活动线程以使 CPU 饱和。值得一提的是,它也ForkJoinPool
利用了工作窃取。
使用哪一个?如果您使用 fork-join 模型,或者您知道您的任务会无限期地阻塞,请使用ForkJoinPool
. 如果您的任务很短并且主要受 CPU 限制,请使用newWorkStealingPool
.
话虽如此,现代应用程序倾向于使用具有可用处理器数量的线程池,并利用异步 IO和无锁容器来防止阻塞。这(通常)提供最佳性能。