在线程之间划分不均匀的数字

时间:2016-04-18 08:28:26

标签: java arrays multithreading algorithm java-threads

我刚学习Java中的Threads,我想按字母顺序排列单词列表。我的程序读取txt文件的文字并将它们放在一个String-array中。用户可以选择他们想要自己使用的线程数。我希望将数组拆分为偶数(尽可能)的线程,线程可以自行排序。

所以我的问题:

如何在线程中尽可能地分割array.length?我的想法是空白,我想不出一个聪明的方法来做到这一点。

例如:如果我有一个22和4个线程的array.length,在这种情况下如何给出线程; 6,6,5和5个大小的阵列?需要适用于给出的每个号码。

我尽力解释它,请问是否有不明确的事情!谢谢!

6 个答案:

答案 0 :(得分:5)

它不需要尽可能均匀。如果一个线程有6个,这将决定它花费的时间长度,在这种情况下,最多6个线程无关紧要。

你可以做到

int chunkSize = (tasks + threads - 1) / threads; // divide by threads rounded up.
for (int t = 0; t < threads; t++) {
    int start = t * chunksSize;
    int end = Math.min(start + chunkSize, tasks);
    executor.submit(() -> {
         // inside the thread
         for (int i = start; i < end; i++) {
             process(i);
    });
}

注意:如果你使用Stream.of(array).parallel(),它实际上每个线程创建两个任务。这减轻了一些批次可能需要更长时间,即使它们具有相同数量的元素。

答案 1 :(得分:5)

让我举一个例子,因为它很容易解释。 4个线程中的22个元素。

22%4 = 2.这为您提供了比剩余线程更多地获得一个元素的线程数。

22/4 = 5.这为每个线程提供了最少的元素数。

现在开始将你的数组分成5个元素并将它们分配给一个线程,直到你留下(22%4)2个线程。分别为它们分配剩余的(5 + 1 = 6)元素。

答案 2 :(得分:0)

您可以分两个阶段完成。 首先:用线程计数除长度而不用剩余部分得到块。 第二:将剩余部分分成块 - 每个块+1。一些块不会获得+1。

答案 3 :(得分:0)

给定n个元素和k个线程,您应该将1 + n/k元素分配给第一个n % k个线程,并将n/k个元素分配给剩余的线程。< / p>

在您的情况下,您有n = 22k = 4,因此...... n/k = 5(向下舍入)和n%k = 2,因此首先2个帖子有分配给5+1个元素,剩下的2个帖子分配了5

答案 4 :(得分:0)

为了确保线程具有“类似”工作负载,找到均匀分布非常重要。当线程数与元素数量相比“高”时,这尤其重要。对于这种情况,应该确保线程负责的元素数量最多相差1个。

为了实现这一点,您可以计算剩余的除以元素数量(在您的情况下为数组长度)的线程数,并在任务中逐个分配此余数。

我前一段时间遇到过同样的问题。实际上,我尝试以稍微更一般的形式解决它,对于某些ParallelRangeExecutor类,它需要计算 start - 和 end 索引。任意范围的间隔(不需要以索引0开头)。以下是从这个类中“提取”的:

import java.util.Arrays;

public class EvenTaskDistribution
{
    public static void main(String[] args)
    {
        test( 22, 4);
        test( 21, 4);
        test(100, 3);
        test(  3, 4);
    }

    private static void test(int numElements, int parallelism)
    {
        int taskSizes[] = computeTaskSizes(parallelism, 0, numElements);
        System.out.printf("Distributing %4d elements among %4d threads: %s\n",
            numElements, parallelism, Arrays.toString(taskSizes));
    }

    public static int[] computeTaskSizes(
        int parallelism, int globalMin, int globalMax)
    {
        if (parallelism <= 0)
        {
            throw new IllegalArgumentException(
                "Parallelism must be positive, but is " + parallelism);
        }
        if (globalMin > globalMax)
        {
            throw new IllegalArgumentException(
                "The global minimum may not be larger than the global " + 
                "maximum. Global minimum is "+globalMin+", " + 
                "global maximum is "+globalMax);
        }
        int range = globalMax - globalMin;
        if (range == 0)
        {
            return new int[0];
        }
        int numTasks = Math.min(range, parallelism);
        int localRange = (range - 1) / numTasks + 1;
        int spare = localRange * numTasks - range;
        int currentIndex = globalMin;
        int taskSizes[] = new int[numTasks];
        for (int i = 0; i < numTasks; i++)
        {
            final int min = currentIndex;
            final int max = min + localRange - (i < spare ? 1 : 0);
            taskSizes[i] = max - min; 
            currentIndex = max;
        }
        return taskSizes;
    }
}

输出

Distributing   22 elements among    4 threads: [5, 5, 6, 6]
Distributing   21 elements among    4 threads: [5, 5, 5, 6]
Distributing  100 elements among    3 threads: [33, 33, 34]
Distributing    3 elements among    4 threads: [1, 1, 1]

(最后一个显示了一个可能需要考虑的极端情况。例如,人们可以期待[1,1,1,0]。但这可以根据应用案例轻松调整。)

答案 5 :(得分:0)

  • @MS Srikkanth 观点的实现。

    {
         int threadCount = 4;
         ExecutorService executorService = Executors.newFixedThreadPool(threadCount);
         int numberOfTasks = 22;
         int chunkSize = numberOfTasks / threadCount;
         int extras = numberOfTasks % threadCount;
    
         int startIndex, endIndex = 0;
    
         for(int threadId = 0; threadId < threadCount; threadId++){
             startIndex = endIndex;
             if(threadId < (threadCount-extras)) {
                 endIndex = Math.min(startIndex + chunkSize, numberOfTasks);
             }else{
                 endIndex = Math.min(startIndex + chunkSize + 1, numberOfTasks);
             }
    
    
             int finalStartIndex = startIndex;
             int finalEndIndex = endIndex;
             executorService.submit(() -> {
                 log.info("Running tasks from startIndex: {}, to endIndex: {}, total : {}", finalStartIndex, finalEndIndex-1, finalEndIndex-finalStartIndex);
                 for (int i = finalStartIndex; i < finalEndIndex; i++) {
                      process(i);
                 }
             });
         }
         executorService.shutdown();
    }