最佳方法:树集结构 vs 线程池执行器

Posted

技术标签:

【中文标题】最佳方法:树集结构 vs 线程池执行器【英文标题】:Best approach: tree set structure vs thread pool executor 【发布时间】:2014-11-26 07:03:20 【问题描述】:

伙计们,我在Tree SetThread Pool Executor 之间进退两难

以下是场景:

第一种方法

    我必须使用其中包含任务的结构以及每个任务的优先级。现在基于treeset constructor(带有comparator 接口) 我可以比较任务的优先级,并据此对任务进行正确排序。 现在之后,任务应该通过树集的迭代按优先级顺序处理,并一个一个地执行每个任务。

第二种方法

    第二种方法是进行某种逻辑构建并使用Thread pool executor 的核心功能,为此我从this link 中获得了灵感,并且我通过这种方法实现了我的要求,它将首先选择高优先级任务,然后首先执行它,它会以同样的方式执行所有任务。

现在我的困惑是在性能成本、灵活性(增加/减少线程)等方面最好使用哪一个,我为什么要选择它?

非常感谢任何建议和答案。

【问题讨论】:

我不认为线程池优先级可以保证让那些线程先行,所以你的树集听起来更好。 我见过的大多数执行器示例都使用队列。为什么不将PriorityQueue 与相同的比较器一起使用?询问哪个是“最好的”实际上取决于您的用例和工作量,因为没有一个答案总是适用于您的特定任务。 @Evan 知道我已经制作了thread pool executor 模型的原始原型,如果您想要输出,您可以查看上面问题链接中提供的示例,其中包含主要方法示例及其输出使其更具描述性 @Makoto 我已经实施了优先队列,我得到了预期的结果,但是 M 在性能成本和灵活性方面寻找最佳解决方案 @didierc 在this link 的帮助下,我已经完成了任务排序和执行。感谢您的宝贵建议,但我很想知道treesetthread pool executor 之间谁比谁更好. 【参考方案1】:

您的问题中嵌入了两种不同的优先级概念:

起始优先级:任务提交执行的顺序,(第一种方法说明的第 1 点)

运行时优先级:考虑调度线程的顺序(第3点)

这两个属性在您的场景中恰好相等,因此树集将帮助您定义它们。执行器将帮助您执行它们,但您将需要一个特别定制的执行器(基于或不基于线程池),以特定优先级启动您的线程。基本上,每次从优先级队列中拉出任务时,它都应该与设置在任务优先级的线程相关联。我假设这是您链接的文章中的执行程序实现所提供的功能,因此您可以执行此操作。

关于线程池,来自文档:

使用工作线程可以最大限度地减少由于创建线程而产生的开销。线程对象使用大量内存,在大型应用程序中,分配和释放许多线程对象会产生大量内存管理开销。

工作线程是由线程池管理的线程,并且被保守地回收(与销毁和重新创建相反)以处理任务序列。我认为优先级处理并不重要,但它会优化您对资源的使用。

关于文章中的实现,代码使用一个简单的阻塞双端队列来处理传入的任务,因此它是一个普通的 fifo 优先级方案。它不会重新排序任务。

【讨论】:

正是,该特性(线程的优先级与任务的优先级相同)是在第二种方法中实现的。 tree set 方法很好,但我的困惑是时间复杂度、繁重任务负载期间的性能成本、灵活性等,具体取决于选择哪一种。? 啊,对不起,我错过了这一点。我想如果没有具体的实现细节,就很难根据复杂性和成本来评估它们。【参考方案2】:

最终从这两个中获得了真正的赢家。我应该选择Thread pool Executor,原因如下

    性能成本:在这里,如果我们看到,使用资源最大值是在重负载期间获得性能的主要动机。因此,如果我们在这个高时间使用线程,它将提供高性能作为多线程的优势线程。 灵活性:在资源可扩展使用方面的灵活性,即在低时间我们可以减少thread pool executor 架构中的工作线程数量,反之亦然。 迭代次数少,更新少:如果我们每次都维护树集,它会在comparator接口的帮助下检查,虽然它有复杂度O(logn)但是之后我们必须获取它,它将成为单源的顺序流,因此我们将没有多线程环境的优势。 更快的处理:借助线程架构,我们可以实现更快的输出。

等是我在激烈的头脑风暴、谷歌搜索和最后但并非最不重要的堆栈溢出搜索中指出的原因。感谢大家对@didierc 的谦虚支持和极大的感谢,让我明白了这一点。

【讨论】:

【参考方案3】:

你可以在普通线程池中尝试DelayedQueue。

ThreadPoolExecutor threadPoolExecutor = new ThreadPoolExecutor(size, size, 0, TimeUnit.DAYS, new DelayQueue<>());
threadPoolExecutor.execute(runnable);

Runnable 应该是实现 Comparable 。所以在这个实现中,优先级将由延迟队列处理。

这种方法会更容易实现。

【讨论】:

以上是关于最佳方法:树集结构 vs 线程池执行器的主要内容,如果未能解决你的问题,请参考以下文章

线程池的理解及使用

线程池参数调优配置测试方法

自定义线程池,如何最佳创建线程池

使用多个执行程序时,理想/最佳线程池大小是多少?

Java 线程池原理及最佳实践(1.5W字,面试必问)

线程池的理解及使用