簡體   English   中英

使用多線程對 Java 進行奇偶排序

[英]Odd-Even sort Java using multithreading

我是這個小組的新手,所以我相信有可能在這里獲得幫助,因為我在 Google 上找不到關於我的問題的任何信息。

我正在嘗試並行實現 Java EvenOdd 轉置排序。 因此,在我的算法中,我認為划分為分區將是一個好主意:

  1. 如何知道我應該划分我的數組列表多少部分? 例如,我現在使用 17 個元素以使其更易於理解。
  2. 另外,我不知道我是否應該使用名為 ExecutorService 的東西或只是正常創建線程。

我在這里添加我當前的邏輯:從奇數階段開始,分成兩部分並分配兩個線程進行這些比較,然后創建一個屏障來等待線程,因此啟動其他線程以類似地使用偶數索引。 感謝您能給我的任何幫助。 目前,我不知道如何實現這個算法,所以任何話都可能會有所幫助。 在此處輸入圖像描述

歡迎來到 StackOverflow!

如何知道我應該划分我的數組列表多少部分? 例如,我現在使用 17 個元素以使其更易於理解。

您將數組划分為子數組的直覺是正確的,因為它通常是並發排序算法的基礎。 正如你已經知道的算法,我們只需要討論實現:

  1. 直觀的解決方案是為每個奇數索引創建一個threadstart()所有這些索引用於比較和交換,並將它們join()到主線程以等待結果。 沖洗並重復此N次。 然而,這是非常低效的,因為創建和啟動所有O(N^2)線程的開銷對於快速比較和交換來說非常大。
  2. 我們還可以為每個奇數索引創建線程,並讓它們在左右之間反復比較和交換。 這是有問題的,因為我們必須反復鎖定左右(以防止數據競爭),這會導致調度程序產生大量無用的開銷,並且我們不知道何時完成。
  3. 最后,我們可以為每個奇數索引創建線程,也可以讓它們在左右之間反復交替,並且每次都讓它們在屏障上等待。 這對我來說似乎是正確的選擇,因為它最大限度地減少了線程管理開銷,也限制了無用的比較和數據競爭。

這導致我們得到以下代碼:

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();}
        }
    }   
}

請注意,該算法的運行時間為O(n) ,這對於這種並行算法來說並不是最好的。 您可以嘗試並行實現的另一種算法是MergeSort算法。 有很多事情可以用這個並行化,但最重要的是合並,因為它是順序算法的瓶頸。 您可以查看Batcher Odd-Even Mergesort或查看其他並行合並

另外,我不知道我是否應該使用名為 ExecutorService 的東西或只是正常創建線程。

Java 提供了許多不同的並行工具,它們在不同的抽象級別上運行。 可以說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();}
        }
    }   
}

如您所見,我們使用線程工廠newFixedThreadPool靜態方法來生成和實例化所有線程。 然后我們將任務添加到線程池中,線程池將返回一個Future變量。 當線程完成時(在我們的例子中為 null), Future將保存該值。 調用Future.get()方法將等待結果(因此線程完成)。 請注意,您想要實現某種嵌套線程並行(例如,在並行化 MergeSort 時)。 您應該使用ForkJoinPool ,因為它是專門為此而設計的。 最后,是一個關於ExecutorService的好教程。

如果您需要任何詳細信息,請隨時在評論中詢問。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM