简体   繁体   English

基于可用的免费cpu的Java并发

[英]Java concurrency based on available FREE cpu

QUESTION

How do I scale to use more threads if and only if there is free cpu? 当且仅当有空闲cpu时,如何扩展以使用更多线程? Something like a ThreadPoolExecutor that uses more threads when cpu cores are idle, and less or just one if not. 类似于ThreadPoolExecutor的东西,当cpu核心空闲时使用更多线程,如果没有则更少或仅使用一个线程。

USE CASE 使用案例

Current situation: My Java server app processes requests and serves results. 当前情况:我的Java服务器应用程序处理请求并提供结果。 There is a ThreadPoolExecutor to serve the requests with a reasonable number of max threads following the principle: number of cpu cores = number of max threads. 有一个ThreadPoolExecutor按照以下原则为具有合理数量的最大线程的请求提供服务:cpu核心数=最大线程数。 The work performed is cpu heavy, and there's some disk IO (DBs). 执行的工作很重,并且有一些磁盘IO(DB)。 The code is linear, single threaded. 代码是线性的,单线程的。 A single request takes between 50 and 500 ms to process. 单个请求需要50到500毫秒才能处理。 Sometimes there are just a few requests per minute, and other times there are 30 simultaneous. 有时每分钟只有几个请求,有时同时有30个请求。 A modern server with 12 cores handles the load nicely. 具有12个内核的现代服务器可以很好地处理负载。 The throughput is good, the latency is ok. 吞吐量很好,延迟还可以。

Desired improvement: When there is a low number of requests, as is the case most of the time, many cpu cores are idle. 期望的改进:当请求数量较少时,大多数情况下都是如此,许多cpu核心处于空闲状态。 Latency could be improved in this case by running some of the code for a single request multi-threaded. 在这种情况下,可以通过为单个请求多线程运行一些代码来改进延迟。 Some prototyping shows improvements, but as soon as I test with a higher number of concurrent requests, the server goes bananas. 一些原型设计显示了改进,但是一旦我测试了更多的并发请求,服务器就会变成香蕉。 Throughput goes down, memory consumption goes overboard. 吞吐量下降,内存消耗过高。 30 simultaneous requests sharing a queue of 10 meaning that 10 can run at most while 20 are waiting, and each of the 10 uses up to 8 threads at once for parallelism, seems to be too much for a machine with 12 cores (out of which 6 are virtual). 30个同时请求共享队列10意味着10个最多可以运行而20个正在等待,并且10个中的每一个一次最多使用8个线程用于并行,对于具有12个核心的机器来说似乎太多了(其中6是虚拟的)。

This seems to me like a common use case, yet I could not find information by searching. 在我看来,这似乎是一个常见的用例,但我无法通过搜索找到信息。

IDEAS IDEAS

1) request counting One idea is to count the current number of processed requests. 1)请求计数一个想法是计算当前处理的请求数。 If 1 or low then do more parallelism, if high then don't do any and continue single-threaded as before. 如果为1或低则执行更多并行操作,如果为高则不执行任何操作并继续执行单线程,如前所述。 This sounds simple to implement. 这听起来很简单。 Drawbacks are: request counter resetting must not contain bugs, think finally. 缺点是:请求计数器重置不能包含错误,最后想一想。 And it does not actually check available cpu, maybe another process uses cpu also. 它实际上并没有检查可用的cpu,也许另一个进程也使用cpu。 In my case the machine is dedicated to just this application, but still. 在我的情况下,机器专用于这个应用程序,但仍然。

2) actual cpu querying I'd think that the correct approach would be to just ask the cpu, and then decide. 2)实际的cpu查询我认为正确的方法是只询问cpu,然后再决定。 Since Java7 there is OperatingSystemMXBean.getSystemCpuLoad() see http://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/OperatingSystemMXBean.html#getSystemCpuLoad() but I can't find any webpage that mentions getSystemCpuLoad and ThreadPoolExecutor, or a similar combination of keywords, which tells me that's not a good path to go. 由于Java7有OperatingSystemMXBean.getSystemCpuLoad(),请参阅http://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/OperatingSystemMXBean.html#getSystemCpuLoad()但我可以找不到任何提到getSystemCpuLoad和ThreadPoolExecutor的网页,或类似的关键字组合,这告诉我这不是一条好路。 The JavaDoc says "Returns the "recent cpu usage" for the whole system", and I'm wondering what "recent cpu usage" means, how recent that is, and how expensive that call is. JavaDoc说“返回”整个系统最近的cpu使用情况“,我想知道”最近的cpu使用情况“是什么意思,最近的情况以及调用的成本。

UPDATE UPDATE

I had left this question open for a while to see if more input is coming. 我暂时搁置了这个问题,看看是否会有更多的输入。 Nope. 不。 Although I don't like the "no-can-do" answer to technical questions, I'm going to accept Holger's answer now. 虽然我不喜欢技术问题的“无能为力”的答案,但我现在接受霍尔格的回答。 He has good reputation, good arguments, and others have approved his answer. 他有良好的声誉,良好的论据,其他人已经批准了他的答案。 Myself I had experimented with idea 2 a bit. 我自己曾经尝试过2个想法。 I queried the getSystemCpuLoad() in tasks to decide how large their own ExecutorService could be. 我查询任务中的getSystemCpuLoad()来决定他们自己的ExecutorService有多大。 As Holger wrote, when there is a SINGLE ExecutorService, resources can be managed well. 正如Holger所写,当存在SINGLE ExecutorService时,可以很好地管理资源。 But as soon as tasks start their own tasks, they cannot - it didn't work out for me. 但是一旦任务开始他们自己的任务,他们就不能 - 这对我来说没有用。

There is no way of limiting based on “free CPU” and it wouldn't work anyway. 没有办法根据“免费CPU”进行限制,无论如何它都无法工作。 The information about “free CPU” is outdated as soon as you get it. 有关“免费CPU”的信息在您获得后立即过时。 Suppose you have twelve threads running concurrently and detecting at the same time that there is one free CPU core and decide to schedule a sub-task… 假设您有12个并发运行的线程并同时检测到有一个空闲的CPU核心并决定安排子任务......

What you can do is limiting the maximum resource consumption which works quite well when using a single ExecutorService with a maximum number of threads for all tasks. 您可以做的是限制最大资源消耗,当使用具有所有任务的最大线程数的单个ExecutorService ,该消耗非常有效。

The tricky part is the dependency of the tasks on the result of the sub-tasks which are enqueued at a later time and might still be pending due to the the limited number of worker threads. 棘手的部分是任务对子任务的结果的依赖性,这些子任务在以后排队,并且由于工作线程的数量有限,可能仍然处于未决状态。

This can be adjusted by revoking the parallel execution if the task detects that its sub-task is still pending. 如果任务检测到其子任务仍处于挂起状态,则可以通过撤消并行执行来调整此值。 For this to work, create a FutureTask for the sub-task manually and schedule it with execute rather than submit . 为此,请手动为子任务创建FutureTask ,并使用execute而不是submit安排它。 Then proceed within the task as normally and at the place where you would perform the sub-task in a sequential implementation check whether you can remove the FutureTask from the ThreadPoolExecutor . 然后在正常情况下继续执行任务,并在顺序实现中执行子任务的位置检查是否可以从ThreadPoolExecutor remove FutureTask Unlike cancel this works only if it has not started yet and hence is an indicator that there are no free threads. cancel不同,这只有在尚未启动的情况下才有效,因此它是一个没有空闲线程的指示器。 So if remove returns true you can perform the sub-task in-place letting all other threads perform tasks rather than sub-tasks. 因此,如果remove返回true ,则可以就地执行子任务,让所有其他线程执行任务而不是子任务。 Otherwise, you can wait for the result. 否则,您可以等待结果。

At this place it's worth noting that it is ok to have more threads than CPU cores if the tasks accommodate I/O operations (or may wait for sub-tasks). 在这个地方值得注意的是,如果任务适应I / O操作(或者可能等待子任务),拥有比CPU核心更多的线程是可以的。 The important point here is to have a limit. 这里重要的是要有一个限制。

FutureTask<Integer> coWorker = new FutureTask<>(/* callable wrapping sub-task*/);
executor.execute(coWorker);

// proceed in the task’s sequence

if(executor.remove(coWorker)) coWorker.run();// do in-place if needed
subTaskResult=coWorker.get();

// proceed

It sounds like the ForkJoinPool introduced in Java 7 would be exactly what you need. 听起来像Java 7中引入的ForkJoinPool正是您所需要的。 The ForkJoinPool is specifically designed to keep all your CPUs exactly busy meaning that there are as many threads as there are CPUs and that all those threads are also working and not blocking (For the later make sure that you use ManagedBlocker s for DB queries). ForkJoinPool专门设计用于使所有CPU完全忙碌,这意味着存在与CPU一样多的线程,并且所有这些线程也在工作而不是阻塞(对于后者,请确保使用ManagedBlocker进行数据库查询)。

In a ForkJoinTask there is the method getSurplusQueuedTaskCount for which the JavaDoc says "This value may be useful for heuristic decisions about whether to fork other tasks." ForkJoinTask有一个方法getSurplusQueuedTaskCount ,JavaDoc getSurplusQueuedTaskCount说“这个值可能对于是否分叉其他任务的启发式决策很有用。” and as such serves as a better replacement for your getSystemCpuLoad solution to make decisions about task decompositions. 因此可以更好地替代getSystemCpuLoad解决方案,以便对任务分解做出决策。 This allows you to reduce the number of decompositions when system load is high and thus reduce the impact of the task decomposition overhead. 这允许您在系统负载较高时减少分解次数,从而减少任务分解开销的影响。

Also see my answer here for some more indepth explanation about the principles of Fork/Join-pools. 另请参阅我的答案以获得有关Fork / Join-pools原理的更深入的解释。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM