如何根据CPU内核扩展线程?


107

我想用Java中的多个线程解决数学问题。我的数学问题可以分为多个工作单元,我想通过几个线程来解决。

我不希望有固定数量的线程在工作,而是与CPU核心数量匹配的线程数量。我的问题是,为此我在互联网上找不到简单的教程。我发现的都是带有固定线程的示例。

如何才能做到这一点?你能提供例子吗?

Answers:


119

您可以通过使用静态运行时方法,确定提供给Java虚拟机的进程数availableProcessors。确定可用处理器的数量后,请创建该数量的线程,并相应地拆分您的工作。

更新:为了进一步阐明,线程只是Java中的一个对象,因此您可以像创建任何其他对象一样创建它。因此,假设您调用了上面的方法并发现它返回了2个处理器。太棒了 现在,您可以创建一个循环,该循环生成一个新的线程,并拆分该线程的工作,然后触发该线程。这是一些伪代码来说明我的意思:

int processors = Runtime.getRuntime().availableProcessors();
for(int i=0; i < processors; i++) {
  Thread yourThread = new AThreadYouCreated();
  // You may need to pass in parameters depending on what work you are doing and how you setup your thread.
  yourThread.start();
}

有关创建自己的线程的更多信息,请转到本教程。另外,您可能需要查看线程池以创建线程。


17
这基本上是正确的,但请注意使用英特尔“超线程”销售的处理器的性能。在四核
处理器上

嗨,好的,不知道,这是可能的。但是,当我将一项任务拆分为多个工作单元,并且需要最终解决方案的所有部分解决方案时,该如何完成?当我有几个“ yourThreads”时,如何使用join(),因为我看不到,所以这几个线程是如何区分的?:)顺便说一句:您对线程池的链接将我带到ibm.com/developerworks/library/j-jtp0730.html:)
Andreas Hornig,2009年

5
在这里查看示例:java.sun.com/j2se/1.5.0/docs/api/java/util/concurrent / ...它将告诉您一种更简化的创建和管理线程池的方式...似乎刚开始时比较复杂,但是与大多数情况一样,它也更复杂,因为如果简单的话,您很快就会遇到限制。
Bill K 2010年

62

您可能也想看看java.util.concurrent框架。就像是:

ExecutorService e = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());
// Do work using something like either
e.execute(new Runnable() {
        public void run() {
            // do one task
        }
    });

要么

    Future<String> future = pool.submit(new Callable<String>() {
        public String call() throws Exception {
            return null;
        }
    });
    future.get();  // Will block till result available

这比应付自己的线程池等要好得多。


嗨,DaveC,嗯,以前还不知道,所以我来看看。可以根据可用的cpu内核进行缩放吗?因为我在简短的示例中看不到这一点。最好的问候,Andreas
Andreas Hornig

3
java.util.concurrent具有高度可伸缩性
Kristopher Ives

4
具有可用处理器数量的固定大小的池通常是CPU绑定进程的最佳选择。这里的第一个示例就是您需要做的所有事情。
彼得·劳瑞

1
如公认答案的第一条评论所述,最好使用报告的“处理器”数量的一半,原因有两个:1.如果您具有超线程,则处理器的实际数量是报告数量的一半。和2.它为系统的其余部分(操作系统和其他程序)提供了一定的处理能力。
Matthieu

10

选项1:

来自的newWorkStealingPoolExecutors

public static ExecutorService newWorkStealingPool()

使用所有可用处理器作为目标并行度级别来创建工作窃取线程池。

使用此API,您无需将内核数传递给ExecutorService

通过grepcode实现此API

/**
     * Creates a work-stealing thread pool using all
     * {@link Runtime#availableProcessors available processors}
     * as its target parallelism level.
     * @return the newly created thread pool
     * @see #newWorkStealingPool(int)
     * @since 1.8
     */
    public static ExecutorService newWorkStealingPool() {
        return new ForkJoinPool
            (Runtime.getRuntime().availableProcessors(),
             ForkJoinPool.defaultForkJoinWorkerThreadFactory,
             null, true);
    }

选项2:

来自Executors或的newFixedThreadPool API other newXXX constructors,返回ExecutorService

public static ExecutorService newFixedThreadPool(int nThreads)

将nThreads替换为 Runtime.getRuntime().availableProcessors()

选项3:

线程池执行器

public ThreadPoolExecutor(int corePoolSize,
                      int maximumPoolSize,
                      long keepAliveTime,
                      TimeUnit unit,
                      BlockingQueue<Runnable> workQueue)

Runtime.getRuntime().availableProcessors()作为参数传递给maximumPoolSize



4

标准方法是Runtime.getRuntime()。availableProcessors()方法。在大多数标准CPU上,您将在此处返回最佳线程数(不是实际的CPU内核数)。因此,这就是您要寻找的。

例:

ExecutorService service = Executors.newFixedThreadPool(Runtime.getRuntime().availableProcessors());

不要忘记像这样关闭执行程序服务(否则您的程序将不会退出):

service.shutdown();

这里只是快速概述如何设置基于将来的MT代码(以主题为例,以示说明):

CompletionService<YourCallableImplementor> completionService = 
    new ExecutorCompletionService<YourCallableImplementor>(service);
    ArrayList<Future<YourCallableImplementor>> futures = new ArrayList<Future<YourCallableImplementor>>();
    for (String computeMe : elementsToCompute) {
        futures.add(completionService.submit(new YourCallableImplementor(computeMe)));
    }

然后,您需要跟踪期望的结果数,并按以下方式检索它们:

try {
  int received = 0;
  while (received < elementsToCompute.size()) {
     Future<YourCallableImplementor> resultFuture = completionService.take(); 
     YourCallableImplementor result = resultFuture.get();
     received++; 
  }
} finally {
  service.shutdown();
}

2
关机呼叫应最终尝试
Christophe Roussy

1
@ChristopheRoussy你说的很对,我已经相应地修改了代码段,谢谢!
fl0w

3

在Runtime类上,有一个称为availableProcessors()的方法。您可以使用它来计算您拥有多少个CPU。由于您的程序受CPU限制,因此您可能希望每个可用CPU最多拥有一个线程。


嗨,杰森(Jason)和埃里克(Eric)(我对您的两个答案都使用了一条评论,因为它基本上是相同的)。好的,很高兴检查一下,但这是第一部分。当我拥有核心数量时,我必须使线程与该核心数量一样可变。我在openbook.galileodesign.de/javainsel5/…(德语!)之前尝试了此示例,它使用了固定线程。但是我想在双核环境中使用2核,在四核环境中使用4核进行相同的编程。我不想手动更改。这可能吗?谢谢!:)
Andreas Hornig

@Andreas-查看我对帖子所做的更新。我认为这将有助于澄清问题。
2009年
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.