【问题标题】:How do I sort a list of records using multithreading in java?如何在java中使用多线程对记录列表进行排序?
【发布时间】:2017-01-22 16:04:01
【问题描述】:

我正在自学 Java 中的多线程。我的虚拟示例是我想要排序的大量记录(二维数组)列表。单线程方法是使用循环遍历记录列表和排序。我想多线程我的程序以使用固定数量的线程对我的列表进行排序,在这种情况下为 2。一个线程将对列表的前半部分进行排序,第二个线程将对剩余的一半进行排序。然后我想输出现在排序的记录列表的结果。

如何创建工作线程池并对记录列表进行排序?我需要担心data 是共享资源吗?如何将每个线程的结果返回到原始记录列表?下面是我的代码。

import java.util.*;

class RunnableProcess implements Runnable {
  private int[] data;

  public RunnableProcess(int[] data) {
      this.data = data;
  }

  public void run() {
    try {

      // sort the records this thread has access to
      for (int i = 0; i < data.length; i++) {
        Arrays.sort(data[i]);
      }

    } catch(Exception ex) {
        ex.printStackTrace();
    }
  }
}

class BigData {

  static int[][] data = new int[1000][1000];

  public static void main(String [] args) {


    // Create records 
    for (int i = 0; i < data.length; i++) {
      for (int j = 0; j < data[0].length; j++) {
        data[i][j] = new Random().nextInt(999);
      }
    }

    // Call on two threads to sort the data variable
    // ExecutorService executor = Executors.newFixedThreadPool(2);


   // Python type of idea: Pass half the records to each thread and start
   // java doesn't support this so what is the java way of doing this?

   // Thread thread = new Thread(new RunnableProcess(data[:499]));
   // thread.start();

   // Thread thread = new Thread(new RunnableProcess(data[499:]));
   // thread.start();

  }
}

我对解决此问题的最佳方法持开放态度。

【问题讨论】:

  • 您忘记了需要执行的第三步 - 合并排序后的结果。研究归并排序算法!

标签: java multithreading


【解决方案1】:

Java 不支持以与 python 相同的方式对原生数组进行切片。我们可以接近,使用ArrayList

首先,顺便说一句。您的随机数据生成非常低效。您正在为您生成的每个随机数创建一个新的 Random 数字生成器对象。您只需要一个生成器,如下所示:

Random rnd = new Random();                     // Only created once
for (int i = 0; i < data.length; i++) {
    for (int j = 0; j < data[0].length; j++) {
        data[i][j] = rnd.nextInt(999);
    }
}

一旦你创建了数据,我们可以把这个原生的int[][]二维数组变成一个List的记录,其中每条记录都是一个int[]一维数组:

List<int[]> records = Arrays.asList(data);

请注意,这不会复制数组中的值。它创建数组的List 视图。对data 中存储的值的任何更改都将反映在records 中,反之亦然。

我们这样做,所以我们可以使用List#subList() 方法,将列表分成两个视图。

List<int[]> first_half = records.subList(0, 500);
List<int[]> second_half = records.subList(500, 1000);

同样,这些是视图,由原始列表支持(由原始数组支持)。通过视图所做的更改将反映在原始视图中。

由于我们现在将记录存储在 List 中,而不是数组中,因此我们需要更新 RunnableProcess 以使用这种新格式:

class RunnableProcess implements Runnable {
    private List<int[]> records;

    public RunnableProcess(List<int[]> records) {
        this.records = records;
    }

    @Override
    public void run() {
        // sort the records this thread has access to
        for (int[] record : records) {
            Arrays.sort(record);
        }
    }
}

我们现在将数据划分为两个独立的集合,以及一个可以对每个集合进行操作的RunnableProcess。现在,我们可以启动多线程了。

ExecutorService executor = Executors.newFixedThreadPool(2);

这个执行器服务创建了一个由两个线程组成的池,并且会一遍又一遍地重用这些线程来处理提交给这个执行器的后续任务。因此,您确实不需要需要创建和启动自己的线程。执行者会处理这个。

executor.submit(new RunnableProcess(first_half));
executor.submit(new RunnableProcess(second_half));

由于我们想知道这两个任务何时完成,我们需要保存从executor.submit()返回的Future

Future<?> task1 = executor.submit(new RunnableProcess(first_half));
Future<?> task2 = executor.submit(new RunnableProcess(second_half));

调用Future#get() 等待任务完成,并检索任务的结果。 (注意:由于我们的Runnable没有返回值,所以会返回null的值。)

task1.get();  // Wait for first task to finish ...
task2.get();  // ... as well as the second task to finish.

最后,你需要#shutdown()执行者,否则你的程序可能无法正常终止。

executor.shutdown();

完整示例:

List<int[]> records = Arrays.asList(data);
List<int[]> first_half = records.subList(0, 500);
List<int[]> second_half = records.subList(500, 1000);

ExecutorService executor = Executors.newFixedThreadPool(2);

try {
    Future<?> task1 = executor.submit(new RunnableProcess(first_half));
    Future<?> task2 = executor.submit(new RunnableProcess(second_half));

    task1.get();  // Wait for first task to finish ...
    task2.get();  // ... as well as the second task to finish.
} catch (InterruptedException | ExecutionException e) {
    e.printStackTrace();
}

executor.shutdown();

我需要担心数据是共享资源吗?

在这种情况下,不。您的 data 是一个数组数组。每个线程仅引用data 数组(作为List),以获取对int[] 记录的引用。 data 数组本身没有被修改;只有记录是,但每一个都只被其中一个线程修改。

如何将每个线程的结果返回到原始记录列表?

由于记录是“就地”排序的,您的 data 变量已经包含您的排序记录数组。对Future#get() 的调用确保每个Thread 都已完成处理,以便可以再次从主线程安全地访问数据。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-02-24
    • 2020-12-23
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多