以负载平衡的方式在4个线程上分配10个无限作业(Java)

时间:2016-08-26 09:30:01

标签: java multithreading concurrency threadpool

我有 10个计算工作,接近无限时间。例如:计算PI的下一个数字,solve NP-hard约束满足问题等

我有 4个线程(所以在 8个核的机器上有4个线程的线程池,所以我有一些内核留给避免实时锁定机器和过程)。

使用Java 8,如何在这4个线程中分配这10个作业?

这是个坏主意:

ExecutorService es = Executors.newFixedThreadPool(4);
for (Job j : jobs) {
    es.submit(j);
}

因为4个工作将开始,但没有工作将完成,因此工作5-10永远不会开始。

如果我照顾10分钟,我希望每份工作都运行约4分钟。 20分钟后,每项工作已经运行了大约8分钟,等等。处理这种情况的典型模式是什么? (如果需要,我可以在预设的时间后实现一种暂停计算的方法。)

4 个答案:

答案 0 :(得分:6)

在四个线程之间分配十个作业的任务和仅使用四个CPU的任务(我在这里使用CPU作为核心的同义词以简化)通过您的十个作业有点不同。 / p>

四个帖子

将线程数限制为4并不能保证它们会坚持使用四个CPU而不会使用其他CPU。允许操作系统根据需要在所有可用CPU之间对线程进行洗牌。您唯一可以保证的是,您的程序将无法利用所有CPU资源的50%以上(假设您有8个CPU)。

但你不可能设法利用这50%。尽管您的工作主要是面向CPU,但他们仍有可能不时需要读取和写入内存。当一个线程错过这些读/写上的缓存并等待数据传递给处理器时,该处理器将该线程置于保持状态并可以在另一个线程中完成一些工作。在你的情况下,它将无所事事,只是闲置,直到数据到达。因此,您的CPU很可能未得到充分利用。

如果您决定采用这种方法,您需要将工作分成小任务并将其提供给执行者,正如@James Large所说。您可以使用WorkStealingPool四个线程(如@Alexey Soshin建议的那样),或创建一个包含十个线程的池,并使用Semaphore,其中四个许可和公平设置为true。在后一种情况下,您的线程必须使用循环,在每次迭代开始时获取许可并在结束时释放它们。每次迭代都代表了一小部分工作。

四个CPU

有一些机制可以指定特定的CPU来处理您的任务。

在Linux的进程级别,您可以使用special commands将进程绑定到特定的CPU。这将允许您创建十个线程,让操作系统在四个CPU上进行所有平衡。

在线程级别,您可以尝试OpenHFT中的Java Affinity library。它允许在Java代码中将线程绑定到CPU。问题是没有提醒就不能在四个CPU之间划分十个线程,因此很难平衡它们。

答案 1 :(得分:2)

我认为你正在寻找WorkStealingPool:

static ExecutorService executor = Executors.newWorkStealingPool(4);
private static Map<Integer, AtomicInteger> map = new ConcurrentHashMap<>();

public static void main(String[] args) throws InterruptedException {


    for (int i = 0; i < 10; i++) {
        executor.submit(new Worker(i))  ;
    }

    Thread.sleep(10000);
    System.out.println(map);
}

private static class Worker implements Runnable {
    private final int k;

    public Worker(int k) {
        this.k = k;
    }

    @Override
    public void run() {
        map.putIfAbsent(k, new AtomicInteger(0));
        map.get(k).getAndIncrement();
        executor.submit(new Worker(this.k));

       // Also possible to resubmit current job
       //executor.submit(this);
    }
}

答案 2 :(得分:1)

如果您需要并行执行10个作业 - 只需运行10个线程。

Executors.newFixedThreadPool(4)更改为Executors.newFixedThreadPool(10)

答案 3 :(得分:1)

我对“从未完成的工作”的想法感到有些困扰。我会称之为别的东西,比如“长时间运行的计算”或者......

如果你有十个,并且你只能负担四个线程来处理它们,那么你唯一的选择是将它们分解为完成的有限“子工作”,然后编写一个调度程序,继续将子作业提供给四个可用的线程。

但这将复制线程系统应该为你做的大部分工作。

我只会制作十个帖子。如果您在只有四个可用内核的计算机上运行以运行十个线程,操作系统将自动将长时间运行的作业分解为“子作业”(即时间片),并在四个核心上公平地安排它们