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

Gon*_*hal 9 java concurrency performance multithreading

当且仅当有空闲cpu时,如何扩展以使用更多线程?类似于ThreadPoolExecutor的东西,当cpu核心空闲时使用更多线程,如果没有则更少或仅使用一个线程.

使用案例

当前情况:我的Java服务器应用程序处理请求并提供结果.有一个ThreadPoolExecutor按照以下原则为具有合理数量的最大线程的请求提供服务:cpu核心数=最大线程数.执行的工作很重,并且有一些磁盘IO(DB).代码是线性的,单线程的.单个请求需要50到500毫秒才能处理.有时每分钟只有几个请求,有时同时有30个请求.具有12个内核的现代服务器可以很好地处理负载.吞吐量很好,延迟还可以.

期望的改进:当请求数量较少时,大多数情况下都是如此,许多cpu核心处于空闲状态.在这种情况下,可以通过为单个请求多线程运行一些代码来改进延迟.一些原型设计显示了改进,但是一旦我测试了更多的并发请求,服务器就会变成香蕉.吞吐量下降,内存消耗过高.30个同时请求共享队列10意味着10个最多可以运行而20个正在等待,并且10个中的每一个一次最多使用8个线程用于并行,对于具有12个内核的机器来说似乎太多了(其中6是虚拟的).

在我看来,这似乎是一个常见的用例,但我无法通过搜索找到信息.

IDEAS

1)请求计数一个想法是计算当前处理的请求数.如果为1或低则执行更多并行操作,如果为高则不执行任何操作并继续执行单线程,如前所述.这听起来很简单.缺点是:请求计数器重置不能包含错误,最后想一想.它实际上并没有检查可用的cpu,也许另一个进程也使用cpu.在我的情况下,机器专用于这个应用程序,但仍然.

2)实际的cpu查询我认为正确的方法是只询问cpu,然后再决定.由于Java7有OperatingSystemMXBean.getSystemCpuLoad(),请参阅http://docs.oracle.com/javase/7/docs/jre/api/management/extension/com/sun/management/OperatingSystemMXBean.html#getSystemCpuLoad() 但我可以找不到任何提到getSystemCpuLoad和ThreadPoolExecutor的网页,或类似的关键字组合,这告诉我这不是一条好路.JavaDoc说"返回"整个系统最近的cpu使用情况",我想知道"最近的cpu使用情况"是什么意思,最近的情况以及调用的成本.

UPDATE

我暂时搁置了这个问题,看看是否会有更多的输入.不.虽然我不喜欢技术问题的"无能为力"的答案,但我现在接受霍尔格的回答.他有良好的声誉,良好的论据,其他人已经批准了他的答案.我自己曾经尝试过2个想法.我查询任务中的getSystemCpuLoad()来决定他们自己的ExecutorService有多大.正如Holger所写,当存在SINGLE ExecutorService时,可以很好地管理资源.但是一旦任务开始他们自己的任务,他们就不能 - 这对我来说没有用.

Hol*_*ger 4

没有办法基于 \xe2\x80\x9cfree CPU\xe2\x80\x9d 进行限制,并且无论如何它都不会 \xe2\x80\x99 工作。关于\xe2\x80\x9cfree CPU\xe2\x80\x9d的信息一拿到就已经过时了。假设有十二个线程同时运行,同时检测到有一个空闲的CPU核心,并决定调度一个子任务\xe2\x80\xa6

\n\n

您可以做的是限制最大资源消耗,当使用ExecutorService具有最大线程数的单个线程时,效果非常

\n\n

棘手的部分是任务对子任务结果的依赖性,这些子任务稍后排队,并且由于工作线程数量有限,可能仍处于待处理状态。

\n\n

如果任务检测到其子任务仍处于挂起状态,则可以通过撤销并行执行来调整这一点。为此,请FutureTask手动为子任务创建 并使用execute而不是安排它submit。然后照常在任务中继续,并在顺序实施中执行子任务的位置检查是否可以removeFutureTask. ThreadPoolExecutor与此不同的cancel是,它仅在尚未启动时才起作用,因此表明没有空闲线程。所以如果remove返回true您可以就地执行子任务,让所有其他线程执行任务而不是子任务。否则,您可以等待结果。

\n\n

在这个地方值得注意的是,如果任务容纳 I/O 操作(或者可能等待子任务),那么线程数可以多于 CPU 核心数。这里重要的一点是限制。

\n\n
FutureTask<Integer> coWorker = new FutureTask<>(/* callable wrapping sub-task*/);\nexecutor.execute(coWorker);\n\n// proceed in the task\xe2\x80\x99s sequence\n\nif(executor.remove(coWorker)) coWorker.run();// do in-place if needed\nsubTaskResult=coWorker.get();\n\n// proceed\n
Run Code Online (Sandbox Code Playgroud)\n