为什么ScheduledThreadPoolExecutor只接受固定数量的线程?

36
  • 我可能会想象一些需要花费很长时间的任务,而ScheduledThreadPoolExecutor会为需要运行的其他任务创建额外的线程,直到达到最大线程数为止。

  • 但是似乎我只能为线程池指定固定数量的线程,为什么?

3个回答

11
作为为什么,我也不知道。但我可以想象。
计算机的资源是有限的。并且不是所有的资源都能同时处理。
如果多个进程同时加载文件,它们加载的速度将比按顺序加载要慢(至少在硬盘上如此)。
处理器同时处理多个线程的能力也是有限的。在某些时候,操作系统或JVM花费的时间来切换线程比线程执行其代码所花费的时间还要多。
这是ScheduledThreadPoolExecutor设计的好原因。您可以将任意数量的作业放到队列中,但是从未同时执行多于可以高效运行的作业数量。当然,如何平衡取决于您自己。
如果您的任务是IO密集型的,则将池大小设置较小;如果是CPU密集型的,则设置稍大一些(32个左右)。您还可以创建多个ScheduledThreadPoolExecutor,一个用于IO密集型任务,另一个用于CPU密集型任务。

这取决于I/O绑定任务的类型 - 如果您查询具有缓慢响应的云服务(负载均衡器后面有很多服务器),则可以拥有100个线程。如果您的I/O绑定任务访问HDD,则最好只使用少量线程,以便其他任务也有机会使用它。 - Nishi

7
在进一步了解ScheduledThreadPoolExecutor时,我发现这个link,该链接解释了ScheduledThreadPoolExecutor解决了Timer类的许多问题。而且引入ScheduledThreadPoolExecutor的原因是为了取代Timer(由于Timer存在各种问题)。 我认为传递给ScheduledThreadPoolExecutor的固定线程数的原因在于它解决了这个类所解决的问题之一。也就是说,Timer类仅启动一个线程。虽然它比为每个任务创建一个线程更有效,但它不是最优解决方案。最佳解决方案可能是使用一些线程,介于为所有任务创建一个线程和为每个任务创建一个线程之间。实际上,最好的解决方案是将任务放置在线程池中。线程池中的线程数应该在构建期间可分配,以允许程序确定线程池中的最佳线程数。
在我看来,这就是 SchecduledThreadPoolExecutor 的用例所在。对于你的情况,你应该能够根据计划安排的任务和这些任务完成所需的时间来决定最佳值。如果我有 4 个长时间运行的任务同时被调度,我会更喜欢我的池大小大于 4,如果在同一时间还有其他任务需要执行。我也会更喜欢将长时间运行的任务分开到不同的执行器中,如早期答案所示。
希望这可以帮助 :)

4
根据Java Concurrency In Practice,无界线程创建的缺点如下:
线程生命周期开销
线程创建和销毁并非免费。线程创建需要时间,并且需要JVM和操作系统进行一些处理活动。
资源消耗
活动线程会消耗系统资源,尤其是内存。当可运行的线程比可用处理器多时,线程会处于空闲状态。有许多空闲线程可能会占用大量内存,给垃圾收集器带来压力,而且许多线程争夺CPU也会产生其他性能成本。如果您有足够的线程使所有CPU保持繁忙状态,则创建更多的线程将没有帮助,甚至可能造成伤害。
稳定性
可以创建的线程数量有限。该限制因平台而异,并受JVM调用参数、Thread构造函数中请求的堆栈大小和底层操作系统对线程的限制等因素的影响。当达到这个限制时,最有可能的结果是OutOfMemoryError。尝试从这样的错误中恢复是非常危险的;更容易的方法是构建您的程序以避免达到此限制。
在某个程度上,增加线程数量可以提高吞吐量,但超过一定限制后,创建更多线程只会减慢应用程序的速度,而创建一个过多的线程可能会导致整个应用程序崩溃。避免危险的方法是对应用程序创建的线程数设置一些限制,并彻底测试应用程序以确保即使达到此限制时,它也不会耗尽资源。
在原型设计和开发期间,无限制地创建线程可能看起来很好用,但问题只有在应用程序部署并承受重负载时才会浮出水面。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接