Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Designing a multi-thread matrix in Java

I have a matrix that implements John Conway's life simulator in which every cell represents either life or lack of it.

Every life cycle follows these rules:

  1. Any live cell with fewer than two live neighbors dies, as if caused by under-population.

  2. Any live cell with two or three live neighbors lives on to the next generation.

  3. Any live cell with more than three live neighbors dies, as if by overcrowding.

  4. Any dead cell with exactly three live neighbors becomes a live cell, as if by reproduction.

Every cell will have a thread that will perform the changes by the rules listed above.

I have implemented these classes:

import java.util.Random;

public class LifeMatrix {
    Cell[][] mat;
    public Action currentAction = Action.WAIT_FOR_COMMAND;
    public Action changeAction;

    public enum Action {
        CHECK_NEIGHBORS_STATE,
        CHANGE_LIFE_STATE,
        WAIT_FOR_COMMAND
    }

    // creates a life matrix with all cells alive or dead or random between dead or alive
    public LifeMatrix(int length, int width) {
        mat = new Cell[length][width];

        for (int i = 0; i < length; i++) { // populate the matrix with cells randomly alive or dead
            for (int j = 0; j < width; j++) {
                mat[i][j] = new Cell(this, i, j, (new Random()).nextBoolean());
                mat[i][j].start();
            }

        }
    }

    public boolean isValidMatrixAddress(int x, int y) {
        return x >= 0 && x < mat.length && y >= 0 && y < mat[x].length;
    }

    public int getAliveNeighborsOf(int x, int y) {
        return mat[x][y].getAliveNeighbors();
    }

    public String toString() {
        String res = "";
        for (int i = 0; i < mat.length; i++) { // populate the matrix with cells randomly alive or
                                               // dead
            for (int j = 0; j < mat[i].length; j++) {
                res += (mat[i][j].getAlive() ? "+" : "-") + "  ";
            }
            res += "\n";
        }
        return res;
    }


    public void changeAction(Action a) {
        // TODO Auto-generated method stub
        currentAction=a;
        notifyAll();                 //NOTIFY WHO??
    }
}

/**
 * Class Cell represents one cell in a life matrix
 */
public class Cell extends Thread {
    private LifeMatrix ownerLifeMat; // the matrix owner of the cell
    private boolean alive;
    private int xCoordinate, yCoordinate;

    public void run() {
        boolean newAlive;

        while (true) {
            while (! (ownerLifeMat.currentAction==Action.CHECK_NEIGHBORS_STATE)){
                synchronized (this) {//TODO to check if correct


                try {
                    wait();
                } catch (InterruptedException e) {
                    System.out.println("Interrupted while waiting to check neighbors");
                }}
            }
            // now check neighbors
            newAlive = decideNewLifeState();

            // wait for all threads to finish checking their neighbors
            while (! (ownerLifeMat.currentAction == Action.CHANGE_LIFE_STATE)) {
                try {
                    wait();
                } catch (InterruptedException e) {
                    System.out.println("Interrupted while waiting to change life state");
                };
            }

            // all threads finished checking neighbors now change life state
            alive = newAlive;
        }
    }

    // checking the state of neighbors and
    // returns true if next life state will be alive
    // returns false if next life state will be dead
    private boolean decideNewLifeState() {
        if (alive == false && getAliveNeighbors() == 3)
            return true; // birth
        else if (alive
                && (getAliveNeighbors() == 0 || getAliveNeighbors() == 1)
                || getAliveNeighbors() >= 4)
            return false; // death
        else
            return alive; // same state remains

    }

    public Cell(LifeMatrix matLifeOwner, int xCoordinate, int yCoordinate, boolean alive) {
        this.ownerLifeMat = matLifeOwner;
        this.xCoordinate = xCoordinate;
        this.yCoordinate = yCoordinate;
        this.alive = alive;
    }

    // copy constructor
    public Cell(Cell c, LifeMatrix matOwner) {
        this.ownerLifeMat = matOwner;
        this.xCoordinate = c.xCoordinate;
        this.yCoordinate = c.yCoordinate;
        this.alive = c.alive;
    }

    public boolean getAlive() {
        return alive;
    }

    public void setAlive(boolean alive) {
        this.alive = alive;
    }

    public int getAliveNeighbors() { // returns number of alive neighbors the cell has
        int res = 0;
        if (ownerLifeMat.isValidMatrixAddress(xCoordinate - 1, yCoordinate - 1) && ownerLifeMat.mat[xCoordinate - 1][yCoordinate - 1].alive)
            res++;
        if (ownerLifeMat.isValidMatrixAddress(xCoordinate - 1, yCoordinate) && ownerLifeMat.mat[xCoordinate - 1][yCoordinate].alive)
            res++;
        if (ownerLifeMat.isValidMatrixAddress(xCoordinate - 1, yCoordinate + 1) && ownerLifeMat.mat[xCoordinate - 1][yCoordinate + 1].alive)
            res++;
        if (ownerLifeMat.isValidMatrixAddress(xCoordinate, yCoordinate - 1) && ownerLifeMat.mat[xCoordinate][yCoordinate - 1].alive)
            res++;
        if (ownerLifeMat.isValidMatrixAddress(xCoordinate, yCoordinate + 1) && ownerLifeMat.mat[xCoordinate][yCoordinate + 1].alive)
            res++;
        if (ownerLifeMat.isValidMatrixAddress(xCoordinate + 1, yCoordinate - 1) && ownerLifeMat.mat[xCoordinate + 1][yCoordinate - 1].alive)
            res++;
        if (ownerLifeMat.isValidMatrixAddress(xCoordinate + 1, yCoordinate) && ownerLifeMat.mat[xCoordinate + 1][yCoordinate].alive)
            res++;
        if (ownerLifeMat.isValidMatrixAddress(xCoordinate + 1, yCoordinate + 1) && ownerLifeMat.mat[xCoordinate + 1][yCoordinate + 1].alive)
            res++;
        return res;
    }

}

public class LifeGameLaunch {

    public static void main(String[] args) {
        LifeMatrix lifeMat;
        int width, length, populate, usersResponse;
        boolean userWantsNewGame = true;
        while (userWantsNewGame) {
            userWantsNewGame = false; // in order to finish the program if user presses
                                      // "No" and not "Cancel"
            width = Integer.parseInt(JOptionPane.showInputDialog(
                    "Welcome to John Conway's life simulator! \n"
                            + "Please enter WIDTH of the matrix:"));
            length = Integer.parseInt(JOptionPane.showInputDialog(
                    "Welcome to John Conway's life simulator! \n"
                            + "Please enter LENGTH of the matrix:"));


            lifeMat = new LifeMatrix(length, width);

            usersResponse = JOptionPane.showConfirmDialog(null, lifeMat + "\nNext cycle?");
            while (usersResponse == JOptionPane.YES_OPTION) {
                if (usersResponse == JOptionPane.YES_OPTION) {
                    lifeMat.changeAction(Action.CHECK_NEIGHBORS_STATE);
                } 
                else if (usersResponse == JOptionPane.NO_OPTION) {
                    return;
                }
                // TODO leave only yes and cancel options
                usersResponse = JOptionPane.showConfirmDialog(null, lifeMat + "\nNext cycle?");
            }
            if (usersResponse == JOptionPane.CANCEL_OPTION) {
                userWantsNewGame = true;
            }
        }
    }
}

My trouble is to synchronize the threads: Every cell(a thread) must change its life/death state only after all threads have checked their neighbors. The user will invoke every next life cycle by clicking a button.

My logic, as can be understood from the run() method is to let every cell(thread) run and wait for the right action state that is represented by the variable currentAction in LifeMatrix class and go ahead and execute the needed action.

What I struggle with is how do I pass these messages to the threads to know when to wait and when execute next action?

Any suggestions to change the design of the program are very welcome as long as every cell will be implemented with a separate thread!

like image 215
Max Segal Avatar asked Jun 17 '15 13:06

Max Segal


People also ask

How do you create multiple threads in Java?

We create a class that extends the java. This class overrides the run() method available in the Thread class. A thread begins its life inside run() method. We create an object of our new class and call start() method to start the execution of a thread. Start() invokes the run() method on the Thread object.

Is Java good for multi threading?

Java has great support for multithreaded applications. Java supports multithreading through Thread class. Java Thread allows us to create a lightweight process that executes some tasks. We can create multiple threads in our program and start them.

What is multi thread in Java with example?

Multithreading in Java is a process of executing two or more threads simultaneously to maximum utilization of CPU. Multithreaded applications execute two or more threads run concurrently. Hence, it is also known as Concurrency in Java. Each thread runs parallel to each other.


1 Answers

Using a CyclicBarrier should be easy to understand:

(updated to use 2 Barriers, and make use of inner class to make cell looks shorter and cleaner)

psuedo code:

public class LifeMatrix {
    private CyclicBarrier cycleBarrier;
    private CyclicBarrier cellUpdateBarrier;
    //.....

    public LifeMatrix(int length, int width) {
        cycleBarrier = new CyclicBarrier(length * width + 1);
        cellUpdateBarrier = new CyclicBarrier(length * width);

        // follow logic of old constructor
    }

    public void changeAction(Action a) {
        //....
        cycleBarrier.await()
    }

    // inner class for cell
    public class Cell implements Runnable {
        // ....

        @Override
        public void run() {
             while (...) {
                 cycleBarrier.await();  // wait until start of cycle
                 boolean isAlive = decideNewLifeState();
                 cellUpdateBarrier.await();  // wait until everyone completed
                 this.alive = isAlive;
             }
        }
    }
}
like image 94
Adrian Shum Avatar answered Sep 29 '22 12:09

Adrian Shum