Java 如何确保N个线程以大致相同的速度运行?

Java 如何确保N个线程以大致相同的速度运行?,java,multithreading,join,erlang,simulation,Java,Multithreading,Join,Erlang,Simulation,我正在考虑编写一个物理模拟软件,其中每个物理元素都将在自己的线程中进行模拟 这种方法有几个优点。它在概念上非常接近真实世界的运作方式。将系统扩展到多台机器会容易得多 然而,为了实现这一点,我需要确保所有线程都以相同的速度运行,并对“相同”进行相当自由的解释。彼此之间的距离在1%以内 这就是为什么我不一定需要类似于Thread.join()的解决方案。我不想让一些优步控制的女教师确保所有线程定期相互同步。我只需要能够要求运行时(无论是哪种——可以是Java、Erlang,或者最适合这个问题的任何东

我正在考虑编写一个物理模拟软件,其中每个物理元素都将在自己的线程中进行模拟

这种方法有几个优点。它在概念上非常接近真实世界的运作方式。将系统扩展到多台机器会容易得多

然而,为了实现这一点,我需要确保所有线程都以相同的速度运行,并对“相同”进行相当自由的解释。彼此之间的距离在1%以内

这就是为什么我不一定需要类似于Thread.join()的解决方案。我不想让一些优步控制的女教师确保所有线程定期相互同步。我只需要能够要求运行时(无论是哪种——可以是Java、Erlang,或者最适合这个问题的任何东西)以或多或少相同的速度运行线程

如有任何建议,将不胜感激

更新2009-03-16


我想感谢所有回答这个问题的人,特别是那些回答基本上是“不要这样做”的人。多亏了大家的评论,我现在对我的问题有了更好的理解,我不太确定我是否应该继续我原来的计划。尽管如此,我还是觉得Peter的回答是这个问题本身的最佳答案,这就是我接受它的原因。

我不是线程专家,但线程的全部要点不是它们彼此独立,而且不确定吗?

如果没有协调,你真的无法做到这一点。如果一个元素最终需要比另一个元素更便宜的计算(以一种潜在的不明显的方式)该怎么办

你不一定需要uber控制器——你可以在每个线程中保留一些步进计数器,并有一个全局计数器指示“最慢”的线程。(当每个线程都完成了一些工作时,它必须检查自己是否落后于其他线程,如果落后,则更新计数器。)如果一个线程注意到它比最慢的线程早了很长一段时间,它就可以短暂地等待(可能在监视器上)


只要经常这样做,以避免由于共享数据争用而产生太多的开销,我认为它可以相当好地工作。

为了实现这一点,必须做两件事。您必须确保每个CPU核心有相同数量的线程,并且需要某种类型的同步


这种同步可能相当简单,比如在执行计算时检查每个线程的“cycle done”变量,但您无法避免它。

线程意味着完全独立于彼此运行,这意味着以任何方式同步它们总是一件痛苦的事。在您的情况下,您需要一个中央“时钟”,因为无法告诉VM每个线程应该获得相同数量的。。。UH它应该得到什么?同样数量的内存?可能没关系。同样数量的CPU?您的所有对象是否都非常相似,以至于每个对象都需要相同数量的汇编指令

所以我的建议是使用一个中央时钟,它向每个进程广播时钟信号。每个进程中的所有线程都会读取刻度(应该是绝对的),计算它们看到的最后一个刻度的差值,然后相应地更新它们的内部模型


当一个线程完成更新时,它必须使自己处于睡眠状态;等待下一个滴答声。在Java中,在“tick received”锁上使用wait(),并用“notifyAll()”唤醒所有线程。我认为您在问题中有一个基本的误解,您说:

它在概念上非常接近真实世界的运作方式

现实世界根本不是以线程的方式工作的。大多数机器中的线程不是独立的,实际上甚至不是同步的(操作系统将使用上下文切换)。当大量的
IO
或等待发生时,它们提供了最大的价值


最重要的是,现实世界不会随着更复杂的事情发生而“消耗更多的资源”。想想两个物体从同一高度坠落时的区别,一个物体平稳下落,另一个物体执行某种复杂的翻滚运动……

请不要这样做。线程是一种O/S抽象,允许出现并行执行。对于多核和多核CPU,O/s可以(但不需要)在不同的核之间分配线程

最接近您的可伸缩性愿景(我认为是可行的)的事情是使用工作线程,其大小与您拥有的内核数量大致匹配,并在它们之间分配工作。草稿:定义一个类ActionTick,它对一个粒子进行更新,并让工作线程从共享队列中选择ActionTick进行处理。即使有这样的解决方案,我也看到了一些挑战

  • 线程开销:不同工作线程之间的上下文切换开销。线程本身是昂贵的(如果实际上没有进程那么糟糕的话):使用不同的线程池大小测试性能。在内核数量之外添加更多线程会降低性能
  • 同步成本:您会遇到几个争用点:一次访问工作队列,但更糟糕的是,访问模拟世界。您需要界定每个ActionTick的效果,或者实施大量的锁定/解锁
  • 优化物理的困难。您希望限定每个ActionTick查看的对象/粒子数(距离截止?模拟空间的3D树细分?)。根据仿真域的不同,您可以通过检查项目子集中是否需要任何更改来消除大量工作。在将工作项排入队列之前,进行此类优化比作为分布式算法更容易。但是,模拟的这一部分会成为潜在的可伸缩性瓶颈
  • 复杂性。线程和并发为解决方案引入了多个蠕虫。总是考虑