利用多線程對數組進行歸並排序


多線程處理歸並排序的方法一般為:

假設有n個線程同步處理,就將數組等分成n份,每個線程處理一份,再對最后n個有序數組進行歸並。

為了使對整個算法具有可擴展性,即線程數n可以自定義,筆者將線程類、處理數組類等進行封裝,分為最主要的4個類:Array, Merge, MyThread, MoreThreads,代碼如下:

/*Array.java*/

import java.util.ArrayList;

/**

 * @author duyue

 *

 *         這個類用來處理數組

 *         

 *         原理:

 *         創建待排序數組成功后,需要配合多線程(假設有n個線程)分別排序,

 *         需要將數組盡量等分成n個分數組(保存到列表中),由n個線程分別歸

 *         並排序,並將各個有序數組(再次保存到列表中),最后整合(不歸並

 *         整合)並覆蓋原數組,等待最后歸並。

 */

class Array {

    /**

     * 構造一個保存數組的列表,用於保存分割后的分數組

     */

    static ArrayList<int[]> arrayList = new ArrayList<int[]>();

    /**

     * @param length 數組長度

     * @return 待排序的數組

     */

    static int[] createArray(int length) {

        int[] array = new int[length];

        for (int i = 0; i < length; i++) {

            array[i] = (int) (Math.random() * 10000);

        }

        return array;

    }

    /**

     * @param array 待分割(多線程排序需要)的數組

     * @param num   線程數,即要分割的份數

     */

    static void divideArray(int[] array, int num) {

        int k = 0;                                  //記錄原數組的復制進度,k代表當前數組的復制初始點

        for (int i = 0; i < num; i++) {

            int point = array.length / num;         //分數組的長度

            int[] a = new int[0];                   //保存分數組

            /*考慮到不夠整除的情況,將剩余的項全部放在最后一個分數組中*/

            if (i != num - 1) a = new int[point];

            if (i == num - 1) a = new int[array.length - k];

            /*將array[k, k + a.length -1]復制到a[0, a.length]中*/

            System.arraycopy(array, k, a, 0, a.length);

            arrayList.add(a);                       //把得到的分數組保存在列表中

            k += point;                             //移動復制初始點

        }

    }

    /**

     * @param newArray 由有序分數組整合(不歸並)的新數組

     * @param num      有序分數組的個數,即由num個線程分別排序后得到的數組數,也就是線程數

     */

    static void newArray(int[] newArray, int num) {

        /*原理與divideArray方法相似*/

        int k = 0;                                  //記錄新數組的待整合初始點

        /*把列表元素(即數組)逐個復制到新的數組中*/

        for (int i = 0; i < num; i++) {

            System.arraycopy(arrayList.get(i), 0, newArray, k, arrayList.get(i).length);

            k += arrayList.get(i).length;           //移動待整合初始點

        }

    }

}
/*Merge.java*/

/**

 * @author duyue

 *         

 *         這是對數組進行歸並排序的類

 */

class Merge {

    private int[] temp;                 //暫時存放待排序數組的temp數組

    /**

     * @param a 待排序的數組由構造器傳遞到類中

     */

    Merge(int[] a) {

        temp = new int[a.length];

    }

    public void sort(int[] a) {

        sort(a, 0, a.length - 1);

    }

    public void sort(int[] a, int low, int high) {

        if (low >= high)

            return;

        int mid = low + (high - low) / 2;

        sort(a, low, mid);              //將左半邊排序

        sort(a, mid + 1, high);         //將左半邊排序

        merge(a, low, mid, high);       //歸並結果

    }

    /**

     * @param a 待歸並的數組,其中a[low,mid]和a[mid+1,high]都是有序的

     */

    public void merge(int[] a, int low, int mid, int high) {

        int i = low, j = mid + 1;

        /*將a[low,high]復制到temp[low,high]*/

        System.arraycopy(a, low, temp, low, high - low + 1);

        /*歸並到a[low,high]*/

        for (int k = low; k <= high; k++) {

            if (i > mid)

                a[k] = temp[j++];

            else if (j > high)

                a[k] = temp[i++];

            else if (temp[j] < temp[i])

                a[k] = temp[j++];

            else

                a[k] = temp[i++];

        }

    }

}
/*MyThread.java*/

import java.util.concurrent.CountDownLatch;

/**

 * @author duyue

 *         

 *         這個類用來定義線程,使其能夠對數組進行歸並排序處理

 */

class MyThread extends Thread {

    public int[] aux;                   //定義一個數組,用來保存待處理的數組

    private CountDownLatch latch;       //定義這個類用來等待各個線程都完成工作,再進行下一步操作

    /*通過構造器將待處理的數組傳遞到線程的類中*/

    public MyThread(int[] aux, CountDownLatch latch) {

        this.aux = aux;

        this.latch = latch;

    }

    public void run() {

        Merge mergeThread = new Merge(aux);

        mergeThread.sort(aux);

        latch.countDown();

    }

}
/*MoreThreads.java*/

import java.util.ArrayList;

import java.util.concurrent.CountDownLatch;

/**

 * @author duyue

 *         

 *         本類是多線程處理歸並排序的核心部分。

 *         

 *         原理:

 *         由用戶指定線程數,例如n個線程,將數組分為n份,分別用n個線程對這n個數組進行歸並排序,

 *         得到n個有序分數組,再對這n個有序數組歸並就得出最后的結果。

 *         線程數越多,相應的速度就會越快。

 *         要處理的數組長度越長,多線程與單線程的對比就越大。

 */

class MoreThreads {

    /**

     * @param num 線程數,由用戶定義

     */

    MoreThreads(int num) {

        System.out.println("現在是" + num + "個線程處理歸並排序:");

        int length = 100;                   //數組總長度

        for (int j = 0; j < 6; j++) {

            /*記錄起始時間*/

            long beginTime = System.currentTimeMillis();

            /*創建待排序的數組*/

            int[] myArray = Array.createArray(length);

            /*將數組近乎等分成num份,以便利用多線程對各個數組排序*/

            Array.divideArray(myArray, num);

            /*

             * 對各個數組利用num個線程同步排序。

             * 將num個線程保存在列表threads中,方便將各個線程處理后的數組調出。

             * CountDownLatch類用於等待所有的線程都工作完成后,進行最終的歸並。

             */

            ArrayList<MyThread> threads = new ArrayList<MyThread>();

            CountDownLatch latch = new CountDownLatch(num);

            for (int i = 0; i < num; i++) {

                MyThread thread = new MyThread(Array.arrayList.get(i), latch);

                thread.start();

                threads.add(thread);

            }

            try {

                latch.await();

            } catch (InterruptedException e) {

                e.printStackTrace();

            }

            /*

             * 清空原裝有數組列表中的元素,

             * 將排序后的各個數組從threads列表中調出,添加到數組列表Array中

             */

            Array.arrayList.clear();

            for (int i = 0; i < num; i++) {

                Array.arrayList.add(threads.get(i).aux);

            }

            /*把各個排序后數組規整到長數組中,並對三個有序數組歸並到一個數組中*/

            Array.newArray(myArray, num);

            /*

             * 對有序數組進行歸並

             * 歸並原理:

             * 將第一個有序分數組(即第一個線程排序后的數組)與其下一個有序分數組(即第二個線程

             * 排序后的數組)歸並成一個數組,再把歸並的數組與其下一個有序分數組(即第三個線程排

             * 序后的數組)歸並,依此類推.

             */

            int low = 0;                    //整合后的長數組myArray的首位

            int mid = -1;                   //待歸並的第一個有序分數組的末位

            int high;                       //待歸並的第二個有序分數組的末位

            for (int i = 0; i < num - 1; i++) {

                Merge merge = new Merge(myArray);

                mid = Array.arrayList.get(i).length + mid;

                high = mid + Array.arrayList.get(i + 1).length;

                merge.merge(myArray, low, mid, high);

            }

            /*記錄結束時間*/

            long endTime = System.currentTimeMillis();

            System.out.println(length + "項數組歸並排序的時間:" + (endTime - beginTime) + "ms");

            length = length * 10;

            Array.arrayList.clear();        //清空列表內容,對下一次循環不造成影響

        }

    }

}

運行以下代碼即可測試:

/*TestThread*/

import java.util.Scanner;

/**

 * @author duyue

 *         

 *         這是一個測試類,用於展示結果。

 */

public class TestThread {

    public static void main(String[] args) {

        new MoreThreads(1);

        System.out.println("--------------------------------");

        new MoreThreads(2);

        System.out.println("--------------------------------");

        new MoreThreads(3);

        System.out.println("--------------------------------");

        System.out.println("你還想嘗試更多線程處理歸並排序嗎?(y:yes, n:no)");

        while (true) {

            Scanner in = new Scanner(System.in);

            String s = in.nextLine();

            if (s.equals("n")) {

                System.out.println("byebye!");

                in.close();

                break;

            } else if (s.equals("y")) {

                System.out.println("請輸入要嘗試的線程數:");

                new MoreThreads(in.nextInt());

                System.out.println("--------------------------------");

                System.out.println("你還想嘗試更多線程處理歸並排序嗎?(y:yes, n:no)");

            } else

                System.out.println("輸入錯誤!請重新輸入");

        }

    }

}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM