1. 程式人生 > 實用技巧 >派生 聚合(Fork/join)模式並行化實現生命遊戲

派生 聚合(Fork/join)模式並行化實現生命遊戲

派生 聚合(Fork/join)模式並行化實現生命遊戲

題目描述:

​ 生命遊戲其實是一個零玩家遊戲,它包括一個二維矩形世界,這個世界中的每個方格居住著一個活著的或死了的細胞。一個細胞在下一個時刻生死取決於相鄰八個方格中活著的或死了的細胞的數量。如果相鄰方格活著的細胞數量過多,這個細胞會因為資源匱乏而在下一個時刻死去;相反,如果周圍活細胞過少,這個細胞會因太孤單而死去。

具體規則如下:

1)如果一個細胞周圍有3個細胞為生(一個細胞周圍共有8個細胞),則該細胞為生(即該細胞若原先為死,則轉為生,若原先為生,則保持不變) 。
2) 如果一個細胞周圍有2個細胞為生,則該細胞的生死狀態保持不變;
3) 在其它情況下,該細胞為死(即該細胞若原先為生,則轉為死,若原先為死,則保持不變)

在程式中,使用0代表死,1代表生。

派生聚合模式

一個主UE裡面Fork出多個子的UE,這些子UE並行完成任務,的一部分,等待這一步的全部子UE完成了這些任務之後,我們再繼續分支。

package main;

import java.io.*;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * @author LiuXingWu
 * @create 2020-11-22 16:45
 */
public class ParallelGameOfLive {
    private int threadCounts;    // 執行緒數量
    private CyclicBarrier barrier;    // 障柵
    private ExecutorService threadPool;    // 執行緒池
    private boolean isCovered = false;    // 是否結束

    private int[][] currentGeneration;     // 當前代的細胞陣列
    private int[][] nextGeneration;    // 下一代的細胞陣列
    private int evolutionaryGenerations;     // 進化的代數
    private int length;       // 細胞陣列的長度,細胞生活環境的邊長, 生命遊戲的格子數
    private List<int[]> arrayOfAliveCell = new ArrayList<int[]>();
    private String fileName;    // 檔名稱

    // 建構函式
    public ParallelGameOfLive(int threadCounts, String fileName){
        this.threadCounts = threadCounts;
        this.fileName = fileName;
        this.readInfo();    // 從檔案中讀取內容
        this.currentGeneration = new int[length][length];    // 初始化細胞生活的棋盤
        this.nextGeneration = new int[length][length];     // 初始化棋盤
        this.threadPool = Executors.newFixedThreadPool(threadCounts);
        // 初始化細胞狀態
        for (int[] e : arrayOfAliveCell){
            if (IsCoordinateValid(e[0], e[1])){
                // 確定活著的細胞
                currentGeneration[e[0]][e[1]] = 1;
            }
        }
    }


    // 進化
    public void Evolution(){
        long start = System.currentTimeMillis();
        int beginX = 0;
        int subLength = length / threadCounts;    // 每個執行緒負責的區域大小為:subLength(x) * Length(y)
        int endX = subLength;
        for (int i = 0; i < threadCounts; i++) {
            if (endX >= length) {
                endX = length - 1;
            }
            threadPool.submit(new cellsEvolution(beginX, endX));
            beginX = endX + 1;
            endX += subLength;
        }
        threadPool.shutdown();    //isShutDown當呼叫shutdown()方法後返回為true。
        while (!threadPool.isTerminated());    // isTerminated當呼叫shutdown()方法後,並且所有提交的任務完成後返回為true
        long end0 = System.currentTimeMillis();
//        System.out.println("生命遊戲進化共用時:" + ((end0 - start) / 1000.00) + "秒," + "處理資料執行緒數:" + threadCounts);
        getArrayOfAliveCell();    // 獲取最終存活的細胞資訊
        WriteInfo();    // 將結果寫入檔案
        long end1 = System.currentTimeMillis();
//        System.out.println( "資料來源來自:" + this.fileName + "檔案,"
//                                   + "整個程式執行共用時(包括將結果存入指定檔案中):"
//                                   + ((end1 - start) / 1000.00) + "秒\n");
    }

    /**
     * 內部類實現一代細胞進化的並行化實現
     * 把棋盤豎切為多塊
     */
    public class cellsEvolution implements Runnable {
        int beginX;    // 開始X座標
        int endX;    // 結束X座標

        public cellsEvolution(int beginX, int endX) {
            this.beginX = beginX;
            this.endX = endX;
        }
        @Override
        public void run() {
            for (int i = 0; i < evolutionaryGenerations; i++) {
                isCovered = false;
                cellsTransfrom(beginX, endX);    // 細胞狀態改變
                try {
                    barrier.await();    // 等所有執行緒都到達欄柵,才開始繼續進行,以便進行資料共享
                } catch (InterruptedException e) {
                    e.printStackTrace();
                } catch (BrokenBarrierException e) {
                    e.printStackTrace();
                }
                synchronized (this) {
                    // 等所有執行緒同步後,進行換代
                    if (!isCovered) {
                        currentGeneration = nextGeneration;
                        nextGeneration = new int[length][length];
                        isCovered = true;
                    }
                }
            }
        }
    }

    /**
     * 細胞狀態改變
     * @param beginX
     * @param endX
     */
    public void cellsTransfrom(int beginX, int endX) {
        for (int i = beginX; i < endX; i++) {
            for (int j = 0; j < length; j++) {
                singleCellEvolution(i, j);    // 單細胞進化
            }
        }
    }

    // 檢驗指定的座標是否合理
    public boolean IsCoordinateValid(int x, int y){
        return !((x < 0 || x >= length) || (y < 0 || y >= length) );
    }

    // 判斷指定細胞在下一代的存活狀況(單細胞進化)
    public void singleCellEvolution(int x, int y){
        int aliveSurroundingCells = 0;      // 指定細胞周圍存活的細胞數量
        // 判斷左上角的細胞
        if(IsCoordinateValid(x - 1, y - 1)) {
            if (currentGeneration[x - 1][y - 1] == 1){
                aliveSurroundingCells++;
            }
        }
        // 判斷正上方的細胞
        if(IsCoordinateValid(x, y - 1)) {
            if (currentGeneration[x][y - 1] == 1) {
                aliveSurroundingCells++;
            }
        }
        // 判斷右上角的細胞
        if(IsCoordinateValid(x + 1, y - 1)) {
            if (currentGeneration[x + 1][y - 1] == 1){
                aliveSurroundingCells++;
            }
        }
        // 判斷左邊的細胞
        if(IsCoordinateValid(x - 1, y)) {
            if (currentGeneration[x - 1][y] == 1){
                aliveSurroundingCells++;
            }
        }
        // 判斷右邊的細胞
        if(IsCoordinateValid(x + 1, y)) {
            if (currentGeneration[x + 1][y] == 1){
                aliveSurroundingCells++;
            }
        }
        // 判斷左下角的細胞
        if(IsCoordinateValid(x - 1, y + 1)) {
            if (currentGeneration[x - 1][y + 1] == 1){
                aliveSurroundingCells++;
            }
        }
        // 判斷正下方的細胞
        if(IsCoordinateValid(x, y + 1)) {
            if (currentGeneration[x][y + 1] == 1){
                aliveSurroundingCells++;
            }
        }
        // 判斷右下角的細胞
        if(IsCoordinateValid(x + 1, y + 1)) {
            if (currentGeneration[x + 1][y + 1] == 1){
                aliveSurroundingCells++;
            }
        }
        // 根據指定細胞周圍存活細胞的狀況確定該細胞下下一代的存活情況
        if (aliveSurroundingCells == 3){
            // 周圍有3個活細胞,該細胞無論死活在下一代都會活著
            nextGeneration[x][y] = 1;
        }else if (aliveSurroundingCells == 2){
            // 周圍有2個活細胞,該細胞保持當前狀態到下一代
            nextGeneration[x][y] = currentGeneration[x][y];
        }else{
            // 其他情況,細胞死亡
            nextGeneration[x][y] = 0;
        }
    }

    // 清空活細胞座標陣列
    public void ClearArrayOfAliveCell(){
        arrayOfAliveCell.clear();
    }

    public void getArrayOfAliveCell () {
        arrayOfAliveCell.clear();    // 清空原有內容
        for (int i = 0; i < length; i++) {
            for (int j = 0; j < length; j++) {
                if (currentGeneration[i][j] == 1) {
                    int[] temp = new int[2];
                    temp[0] = i;
                    temp[1] = j;
                    arrayOfAliveCell.add(temp);
                }
            }
        }
    }
    /**
     * 讀取檔案資訊
     */
    public void readInfo() {
        ClearArrayOfAliveCell();
        File file;
        BufferedReader br = null;
        String readData;    // 讀入的內容
        String[] tempString = new String[2];
        int line = 0;
        try {
            file = new File(this.getClass().getResource("").getPath() + this.fileName);
            if (!file.exists()) {
                file.createNewFile();
            }
            br = new BufferedReader(new FileReader(file));
            while ((readData = br.readLine()) != null) {
                line++;
                tempString = readData.split(",");
                if (line == 1) {
                    length = Integer.valueOf(readData);
                } else if (line == 2) {
                    evolutionaryGenerations = Integer.valueOf(readData);
                } else {
                    int[] temp = new int[2];
                    temp[0] = Integer.valueOf(tempString[0]);
                    temp[1] = Integer.valueOf(tempString[1]);
                    arrayOfAliveCell.add(temp);
                }
            }
        } catch (IOException e0) {
            e0.printStackTrace();
        } finally {
            try {
                if (br != null) {
                    br.close();
                }
            }catch (IOException e1) {
                e1.printStackTrace();
            }
        }

    }

    /**
     * 將結果寫入檔案
     */
    public void WriteInfo() {
        File file;
        BufferedWriter bw = null;
        String writeData = "";
        try {
            file = new File( this.getClass().getResource("").getPath() + "resultOf" + this.fileName);
            if (!file.exists()) {
                System.out.println("檔案不存在,建立檔案");
                file.createNewFile();
            }
            bw = new BufferedWriter(new FileWriter(file));
            for (int[] i : arrayOfAliveCell) {
                writeData += i[0] + "," + i[1] + "\n";
            }
            //System.out.print(writeData);
            bw.write(writeData);
        } catch (IOException e0) {
            e0.printStackTrace();
        } finally {
            try {
                if (bw != null) {
                    bw.close();
                }
            } catch (IOException e1) {
                e1.printStackTrace();
            }
        }
    }

    public void showInfo(){
        if (arrayOfAliveCell.size() == 0) {
            System.out.println("已無活細胞存在!");
        } else {
            for (int[] e : arrayOfAliveCell) {
                System.out.println(e[0] + "," + e[1]);
            }
        }

    }

    public static void main(String[] args) {
        long start1, start2, start3, end1, end2, end3;
        start1 = System.currentTimeMillis();
        ParallelGameOfLive game1 = new ParallelGameOfLive(1, "in_100.txt");
        game1.Evolution();    // 進化
        end1 = System.currentTimeMillis();
        System.out.println("生命遊戲進化共用時:" + ((end1 - start1) / 1000.00) + "秒," + "處理資料執行緒數:1\n");

        start2 = System.currentTimeMillis();
        ParallelGameOfLive game2 = new ParallelGameOfLive(2, "in_100.txt");
        game2.Evolution();    // 進化
        end2 = System.currentTimeMillis();
        System.out.println("生命遊戲進化共用時:" + ((end2 - start2) / 1000.00) + "秒," + "處理資料執行緒數:2\n");

        start3 = System.currentTimeMillis();
        ParallelGameOfLive game3 = new ParallelGameOfLive(4, "in_100.txt");
        game3.Evolution();    // 進化
        end3 = System.currentTimeMillis();
        System.out.println("生命遊戲進化共用時:" + ((end3 - start3) / 1000.00) + "秒," + "處理資料執行緒數:4\n");
    }
}