简体   繁体   English

使用多线程对 Java 进行奇偶排序

[英]Odd-Even sort Java using multithreading

I am new to this group, so I believe it is a possibility to get help here since I could not find any information about my question on Google.我是这个小组的新手,所以我相信有可能在这里获得帮助,因为我在 Google 上找不到关于我的问题的任何信息。

I am trying to implement Java EvenOdd transposition sort parallel.我正在尝试并行实现 Java EvenOdd 转置排序。 Therefore, as I algorithm I thought that dividing into partitions would be a great idea:因此,在我的算法中,我认为划分为分区将是一个好主意:

  1. How to know how many parts should I divide my array list?如何知道我应该划分我的数组列表多少部分? For example, I use 17 elements for now to make it more understandable.例如,我现在使用 17 个元素以使其更易于理解。
  2. Also, I do not know if I should use something called ExecutorService or just create threads normally.另外,我不知道我是否应该使用名为 ExecutorService 的东西或只是正常创建线程。

I add my current logic here: Start from the Odd phase and divide into two parts and assign two threads to make these comparisons, after that create a Barrier to wait for threads and therefore start other threads to work with the even indexes similarly.我在这里添加我当前的逻辑:从奇数阶段开始,分成两部分并分配两个线程进行这些比较,然后创建一个屏障来等待线程,因此启动其他线程以类似地使用偶数索引。 Thanks for any help that you could give me.感谢您能给我的任何帮助。 Currently, I do not know how to implement this algorithm, so any words might help.目前,我不知道如何实现这个算法,所以任何话都可能会有所帮助。 在此处输入图像描述

Welcome to StackOverflow !欢迎来到 StackOverflow!

How to know how many parts should I divide my array list?如何知道我应该划分我的数组列表多少部分? For example, I use 17 elements for now to make it more understandable.例如,我现在使用 17 个元素以使其更易于理解。

Your intuition to divide the array into subarrays is correct, as it is often the basis for concurrent sorting algorithms.您将数组划分为子数组的直觉是正确的,因为它通常是并发排序算法的基础。 As you know the algorithm already, we only have to discuss the implementation :正如你已经知道的算法,我们只需要讨论实现:

  1. The intuitive solution would be to create a thread for every odd index, start() all of them for the compare-and-swap and join() them to the main thread to wait on the result.直观的解决方案是为每个奇数索引创建一个threadstart()所有这些索引用于比较和交换,并将它们join()到主线程以等待结果。 Rince and repeat this N times.冲洗并重复此N次。 This is very inefficient, however, as the overhead of creating and starting all of the O(N^2) threads is far to big for the fast compare-and-swap.然而,这是非常低效的,因为创建和启动所有O(N^2)线程的开销对于快速比较和交换来说非常大。
  2. We can also create threads for every odd index, and make them repeatedly compare-and-swap between left and right.我们还可以为每个奇数索引创建线程,并让它们在左右之间反复比较和交换。 This is problematic as we would have to lock left and right repeatedly (to prevent data races), would lead to a lot of useless overhead with the scheduler and we wouldn't know when we are finished.这是有问题的,因为我们必须反复锁定左右(以防止数据竞争),这会导致调度程序产生大量无用的开销,并且我们不知道何时完成。
  3. Lastly, we can create threads for every odd index, also make them repeatedly alternate between left and right and, every time, make them wait on a barrier.最后,我们可以为每个奇数索引创建线程,也可以让它们在左右之间反复交替,并且每次都让它们在屏障上等待。 This seems for me to be the correct option as it minimizes thread management overhead and also limits useless comparisons and data races.这对我来说似乎是正确的选择,因为它最大限度地减少了线程管理开销,也限制了无用的比较和数据竞争。

This leads us to the following code :这导致我们得到以下代码:

import java.util.Arrays;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;

public class OddEvenSort {
    public static void main(String[] args) {
        int[] arr = {83, 71, 72, 26,  6, 81, 53, 72, 20, 35, 40, 79, 3, 90, 89, 52, 30};
        sortArr(arr);
        System.out.println(Arrays.toString(arr));
    }
    
    public static void sortArr(int[] arr) {
        int threadNum = arr.length/2;
        CyclicBarrier barr = new CyclicBarrier(threadNum);
        Thread[] threads = new Thread[threadNum];
        for (int i = 0; i < threadNum; i++) {
            threads[i] = new Thread(new CompareSwapThread(arr, 2*i + 1, barr));
            threads[i].start();
        }
        for (int i = 0; i < threadNum; i++) {
            try {
                threads[i].join();
            } catch (InterruptedException e) {e.printStackTrace();}
        }
    }
}

class CompareSwapThread implements Runnable {
    private int[] arr;
    private int index;
    private CyclicBarrier barr;
    
    public CompareSwapThread(int[] arr, int index, CyclicBarrier barr) {
        this.arr = arr;
        this.index = index;
        this.barr = barr;
    }
    
    @Override
    public void run() {
        for (int i = 0; i < arr.length; i++) {
            if (arr[index - 1] > arr[index]) {
                int t = arr[index - 1];
                arr[index - 1] = arr[index];
                arr[index] = t;
            }
            try {
                barr.await();
            } catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}
            if (index + 1 < arr.length && arr[index] > arr[index + 1]) {
                int t = arr[index];
                arr[index] = arr[index + 1];
                arr[index + 1] = t;
            }
            try {
                barr.await();
            } catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}
        }
    }   
}

Notice that this algorithm has a runtime of O(n) which is not the best for such a parallel algorithm.请注意,该算法的运行时间为O(n) ,这对于这种并行算法来说并不是最好的。 Another algorithm you can try to implement in parrallel is the MergeSort algorithm.您可以尝试并行实现的另一种算法是MergeSort算法。 There are a lot of things you can parallelize with this one, but the most important one is the merging, as it is the bottleneck in the sequential algorithm.有很多事情可以用这个并行化,但最重要的是合并,因为它是顺序算法的瓶颈。 You can look at Batcher Odd-Even Mergesort or look at other parallel merges .您可以查看Batcher Odd-Even Mergesort或查看其他并行合并

Also, I do not know if I should use something called ExecutorService or just create threads normally.另外,我不知道我是否应该使用名为 ExecutorService 的东西或只是正常创建线程。

Java provides a lot of different tools for parallelism, which operate at different levels of abstraction. Java 提供了许多不同的并行工具,它们在不同的抽象级别上运行。 One could say that ExecutorService is more 'high-level' than basic threads, as it simplifies the thread managment.可以说ExecutorService比基本线程更“高级”,因为它简化了线程管理。 It also will optimize scheduling of tasks, as to make execution better.它还将优化任务的调度,以使执行更好。

Here is our implementation, using ExecutorService :这是我们的实现,使用ExecutorService

import java.util.Arrays;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

public class OddEvenSort {
    public static void main(String[] args) {
        int[] arr = {83, 71, 72, 26,  6, 81, 53, 72, 20, 35, 40, 79, 3, 90, 89, 52, 30};
        sortArr(arr);
        System.out.println(Arrays.toString(arr));
    }
    
    public static void sortArr(int[] arr) {
        int threadNum = arr.length/2;
        CyclicBarrier barr = new CyclicBarrier(threadNum);
        ExecutorService exec = Executors.newFixedThreadPool(threadNum);
        Future<?>[] awaits = new Future<?>[threadNum];
        for (int i = 0; i < threadNum; i++) {
            awaits[i] = exec.submit(new CompareSwapThread(arr, 2*i + 1, barr));
        }
        for (int i = 0; i < threadNum; i++) {
            try {
                awaits[i].get();
            } catch (InterruptedException | ExecutionException e) {e.printStackTrace();}
        }
    }
}

class CompareSwapThread implements Runnable {
    private int[] arr;
    private int index;
    private CyclicBarrier barr;
    
    public CompareSwapThread(int[] arr, int index, CyclicBarrier barr) {
        this.arr = arr;
        this.index = index;
        this.barr = barr;
    }
    
    @Override
    public void run() {
        for (int i = 0; i < arr.length; i++) {
            if (arr[index - 1] > arr[index]) {
                int t = arr[index - 1];
                arr[index - 1] = arr[index];
                arr[index] = t;
            }
            try {
                barr.await();
            } catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}
            if (index + 1 < arr.length && arr[index] > arr[index + 1]) {
                int t = arr[index];
                arr[index] = arr[index + 1];
                arr[index + 1] = t;
            }
            try {
                barr.await();
            } catch (InterruptedException | BrokenBarrierException e) {e.printStackTrace();}
        }
    }   
}

As you can see, we are using the thread factory newFixedThreadPool static method to generate and intantiate all the treads.如您所见,我们使用线程工厂newFixedThreadPool静态方法来生成和实例化所有线程。 We then add our tasks to the thread pool, which will return a Future variable.然后我们将任务添加到线程池中,线程池将返回一个Future变量。 A Future will hold the value, when the thread finished (in our case null).当线程完成时(在我们的例子中为 null), Future将保存该值。 Calling the Future.get() method will wait for the result (and thus the thread to be finished).调用Future.get()方法将等待结果(因此线程完成)。 Notice that is you want to implement some sort of nested thread parralelism (for example, when parallelizing MergeSort).请注意,您想要实现某种嵌套线程并行(例如,在并行化 MergeSort 时)。 You should use ForkJoinPool as it is made specifically for that.您应该使用ForkJoinPool ,因为它是专门为此而设计的。 Finally, here is a good tutorial about ExecutorService .最后,是一个关于ExecutorService的好教程。

If you need any detail, feel free to ask in the comments.如果您需要任何详细信息,请随时在评论中询问。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM